spring-3-AOP

自定义注解类
@Retention(RetentionPolicy.RUNTIME),编译是保留运行时注解
1、定义注解类
package anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//此注解依次可以在类 属性 方法进行
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})

//运行时可以查看注解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    //注解的参数
    public String value();
}

  

2、定义dao(模拟映射)

package dao;

import anno.Entity;

@Entity("entity")
public class CityEntity {
    private String id;
    private String name;

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }
}

  

3、创建公共生成sql工具类
package util;

import anno.Entity;
import dao.CityEntity;

public class UtilSql {
    public static String builSqlStr(Object object){

        Class clazz = object.getClass( );

        // 先判断是否存在注解
        if(clazz.isAnnotationPresent(Entity.class)){

            //判断是否是自定义Entity注解
            Entity entity = (Entity) clazz.getAnnotation(Entity.class);

            //获取相关注解传的值
            String tableName = entity.value();
            CityEntity cityEntity = (CityEntity) object;
            String sql = "select * from "+tableName+" where id ='"+cityEntity.getId()+"'  and name = '"+cityEntity.getName()+"'";
            return sql;
        }
       return "没有获取到相关sql";
    }
}

  

4、测试

package test;

import dao.CityEntity;
import util.UtilSql;

public class Test {
    public static void main(String args[]){
        CityEntity cityEntity = new CityEntity();
        cityEntity.setId("1");
        cityEntity.setName("wjw");
        String sql = UtilSql.builSqlStr(cityEntity);
        System.out.println(sql);
    }
}

  

5、测试结果

6、先关项目截图

 
Aop是什么

  与OOP对比,AOP是处理一些横切性问题,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。

 

aop应用场景

  1、日志记录

  2、权限校验

  3、效率检查

  4、事务管理

  5、.......

 

  • Aspect:切面,跨越多个类别的关注点的模块化。事务管理是企业Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,方面是通过使用常规类(基于模式的方法)或使用注释的常规类来实现的 @Aspect注释(@AspectJ样式注释的

    •  
      一定要给spring去管理  抽象  aspectj->类   xml->label
       
  • Join point:连接点,目标对象植入逻辑类中的的点(方法地方),目标对象就是代理对象的原对象。程序执行期间的一个点,例如执行方法或处理异常。在Spring AOP中,连接点始终表示方法执行。

  • Advice:建议,特定连接点的某个方面采取的操作。不同类型的建议包括“周围”,“之前”和“之后”建议。(建议类型将在后面讨论。)许多AOP框架(包括Spring)将建议建模为拦截器并在连接点周围维护一系列拦截器。

    •   
      Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
      After  连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
      After throwing  执行抛出异常的时候
      After (finally)  无论连接点是正常退出还是异常退出,都会执行
      Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。
  • Pointcut:切入点,就是连接点的集合,再主逻辑中的某一分支需要连接N(不定数)个方法,我们可以使用N个连接点或者直接使用切入点执行N个方法匹配连接点的谓词。建议与切入点表达式相关联,并在切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring默认使用AspectJ切入点表达式语言。

  • Introduction:引入(简介),代表类型声明其他方法或字段。Spring AOP允许您向任何建议的对象引入新接口(以及相应的实现)。例如,您可以使用简介使bean实现 IsModified接口,以简化缓存。(介绍被称为AspectJ社区中的类型间声明。)

  • Target Object:目标对象,被代理对象的原对象。由一个或多个方面建议的对象。也称为“建议对象”。由于Spring AOP是使用运行时代理实现的,因此该对象始终是代理对象。

  • AOP proxy:AOP代理,由AOP框架创建的对象,用于实现方面契约(建议方法执行等)。在Spring Framework中,AOP代理是JDK动态代理或CGLIB代理。

  • Weaving:编织,将方面与其他应用程序类型或对象链接以创建建议对象。这可以在编译时(例如,使用AspectJ编译器),加载时间或在运行时完成。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织

 

各种连接点joinPoint的意义:
   1、execution
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如public,protected;
ret-type-pattern:方法的返回值类型,如int,void等;
declaring-type-pattern:方法所在类的全路径名,如com.spring.Aspect;
name-pattern:方法名类型,如buisinessService();
param-pattern:方法的参数类型,如java.lang.String;
throws-pattern:方法抛出的异常类型,如java.lang.Exception;
example:
@Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的任意方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和类的public方法
@Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和类的public 无方法参数的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的任意接口和类的第一个参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和类的只有一个参数,且参数为String类型的方法
@Pointcut("execution(public * *(..))")//匹配任意的public方法
@Pointcut("execution(* te*(..))")//匹配任意的以te开头的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-example

2、within
™表达式的最小粒度为类
// within与execution相比,粒度更大,仅能实现到包和接口、类级别。而execution可以精确到方法的返回值,参数个数、修饰符、参数类型等
@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法
@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

3、args
args表达式的作用是匹配指定参数类型和指定参数数量的方法,与包名和类名无关
/**
 * args同execution不同的地方在于:
 * args匹配的是运行时传递给方法的参数类型
 * execution(* *(java.io.Serializable))匹配的是方法在声明时指定的方法参数类型。
 */
@Pointcut("args(java.io.Serializable)")//匹配运行时传递的参数类型为指定类型的、且参数个数和顺序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一个参数,并且传递的参数的运行时类型具有@Classifie

   4、this JDK代理时,指向接口和代理类proxy,cglib代理时 指向接口和子类(不使用proxy)

   5、target 指向接口和子类

/**
 * 此处需要注意的是,如果配置设置proxyTargetClass=false,或默认为false,则是用JDK代理,否则使用的是CGLIB代理
 * JDK代理的实现方式是基于接口实现,代理类继承Proxy,实现接口。
 * 而CGLIB继承被代理的类来实现。
 * 所以使用target会保证目标不变,关联对象不会受到这个设置的影响。
 * 但是使用this对象时,会根据该选项的设置,判断是否能找到对象。
 */
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目标对象,也就是被代理的对象。限制目标对象为com.chenss.dao.IndexDaoImpl类
@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//当前对象,也就是代理对象,代理对象时通过代理目标对象的方式获取新的对象,与原值并非一个
@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目标对象中的任意方法
@Pointcut("@within(com.chenss.anno.Chenss)")//等同于@targ
proxy模式里面有两个重要的术语
proxy Class
target Class
CGLIB和JDK有区别    JDK是基于接口   cglib是基于继承所有this可以在cglib作用



根据官网写的简单切入点测试

1、javaConfig
package com.appconfig;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//声明这是一个javaconfig
@Configuration
//扫描com包下的所有类
@ComponentScan("com")
//允许使用AdpectJ自动代理
@EnableAspectJAutoProxy

//javaConfig
public class Appconfig {
}

  

2、模拟持久层用的查询

package com.dao;
import org.springframework.stereotype.Component;


@Component
public class Wjw_Dao {

    public void query(){
        System.out.println("我是query");
    }
}

  

3、切入点类

package com.appconfig;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//需要由容器管理
@Component

//说明这是一个切面类
@Aspect
public class AspectjConfig {

    //连接点对哪些方法进行连接
    @Pointcut("execution(* com.dao.*.*(..))")
    public void pointcut(){

    }

    //每次调用query之前需要处理的事情
    @Before("pointcut()")
    public void befor(){
        System.out.println("我是切面再逻辑之前打印");
    }
}

  

4、测试

import com.dao.Wjw_Dao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String args[]){
        AnnotationConfigApplicationContext acc = new AnnotationConfigApplicationContext(Appconfig.class);
        Wjw_Dao wd = acc.getBean(Wjw_Dao.class);
        wd.query();
    }
}

  

5、截图

相关扩展点:自动代理(spring默认使用的是标准JDK中的proxy)

  如果使用EnableAspectAutoProxy注解需要注意的是,当一个接口的实现类需要使用切入点或者连接点的时候需要设置默认参数为true

  如果不设置则会出现找不到对应的代理对象类型(很奇怪的一个问题)

  @EnableAspectAutoProxy(proxyTargetClass=true) 

  为什么要将默认参数设置为true?

  因为此时生成的对象是proxy和Dao,唯独不是class对象,设置完后将使代理后的对象为目标对象

  我们可以生成相关代理对象的字节码的文件进行查看。

  

public static void main (String args []){
Class<?>[] interfaces = new Class[]{Dao.class};
byte bytes [] = ProxyGenerator.generateProxyClass("wjwTest",interfaces);
File file = new File("D:\test.class");
try{
FileOutputStream fw = new FileOutputStream(file);
fw.write(bytes);
fw.flush();
fw.close();
System.out.println("已完成");
}catch(IOException e){
System.out.println("exception");
e.getMessage();
}

  

扩展二,为什么javaJDK动态代理只能接口,而不能使用继承?

 


原文地址:https://www.cnblogs.com/gnwzj/p/11086945.html