Spring-AOP

1 篇文章 0 订阅

自定义注解

@Target(ElementType.TYPE): 注解的作用域

还有其他类型如下所示:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */ 
    //类上
    TYPE,

    /** Field declaration (includes enum constants) */
    //成员函数上
    FIELD,

    /** Method declaration */
    //方法名称上
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

@Retention(RetentionPolicy.RUNTIME):注解的生命周期

其他的类型如下所示:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
     //仅仅保留在源代码中,编译时期不会编译改代码,仅仅其注释作用
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
     //编译时期会存在,但是运行时期忽略不计
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
     //运行时期也会存在,会一直存在
    RUNTIME
}

  • 自定义注解代码如下:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    String value() default "";  //只有一个参数时,且属性为Value,其可以不写,其他的必须要写
}
@Entity("city")
public class CityEntity {
    private String id;
    private String name;

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

public class ComUtil {

    public static String buildQuerySqlForEntity(Object object){
//        需求:返回sql语句:select * from city where id="1" and name = "test";
        //目标需要将Entity的java对象与city关联起来。成员变量能够对应表中的列
        Class<?> aClass = object.getClass();

        //step1:判断是否加了这个注解
        if(aClass.isAnnotationPresent(Entity.class)){
            //step2: 得到注解
            Entity entity = aClass.getAnnotation(Entity.class);
            String entityName = entity.value();

            System.out.println(entityName);

        }
        return "";
    }
}

spring AOP常见面试题目

Aop是什么

与OOP对比,面向切面,传统的OOP开发中的代码逻辑是自上而下的,而这些过程会产生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不大,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护。AOP是处理一些横切性问题,AOP的编程思想就是把这些问题和主业务逻辑分开,达到与主业务逻辑解耦的目的。使代码的重用性和开发效率更高。

aop的应用场景

  1. 日志记录
  2. 权限验证
  3. 效率检查
  4. 事务管理
  5. exception
NameJDK动态代理CGLIB代理
编译时期的织入还是运行时期的织入?运行时期织入运行时期织入
初始化时期织入还是获取对象时期织入?初始化时期织入初始化时期织入

spring Aop的概念


aspect:一定要给spring去管理 抽象 aspectj->类
pointcut:切点表示连接点的集合 -------------------> 表
(我的理解:PointCutJoinPoint的谓语,这是一个动作,主要是告诉通知连接点在哪里,切点表达式决定JoinPoint的数量)
Joinpoint:连接点 目标对象中的方法 ----------------> 记录
(我的理解:JoinPoint是要关注和增强的方法,也就是我们要作用的点)
Weaving :把代理逻辑加入到目标对象上的过程叫做织入
target目标对象 原始对象
aop Proxy代理对象 包含了原始对象的代码和增加后的代码的那个对象
advice:通知 (位置 + logic)


advice通知类型:
Before 连接点执行之前,但是无法阻止连接点的正常执行,除非该段执行抛出异常
After 连接点正常执行之后,执行过程中正常执行返回退出,非异常退出
After throwing 执行抛出异常的时候
After (finally) 无论连接点是正常退出还是异常退出,都会执行
Around advice: 围绕连接点执行,例如方法调用。这是最有用的切面方式。around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续加入点还是通过返回自己的返回值或抛出异常来快速建议的方法执行。

  • ProceedingjoinpointJoinPoint的区别:
    Proceedingjoinpoint 继承了JoinPoint,proceed()这个是aop代理链执行的方法。并扩充实现了proceed()方法,用于继续执行连接点。JoinPoint仅能获取相关参数,无法执行连接点。

  • JoinPoint的方法

  1. java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
  2. Signature getSignature() :获取连接点的方法签名对象;
  3. java.lang.Object getTarget() :获取连接点所在的目标对象;
  4. java.lang.Object getThis() :获取代理对象本身;

proceed()有重载,有个带参数的方法,可以修改目标方法的的参数

Introductions
perthis
使用方式如下:
@Aspect("perthis(this(com.chenss.dao.IndexDaoImpl))")
要求:

  1. AspectJ对象的注入类型为prototype
  2. 目标对象也必须是prototype的
    原因为:只有目标对象是原型模式的,每次getBean得到的对象才是不一样的,由此针对每个对象就会产生新的切面对象,才能产生不同的切面结果

  1. 原型为多类型,切面为多类型,hashcode都不同
@Repository("indexDao")
@Scope("prototype")
public class IndexDao implements Dao{

    public void query(String str){
        System.out.println("query1");
        System.out.println(str);
    }
    public void query(){
        System.out.println("query2");
    }
}
@Component   //申明是一个Bean
@Aspect
@Aspect("perthis(this(com.luban.dao.IndexDao))")
@Scope("prototype")
public class LubanAspectj {
    @Pointcut("within(com.luban.dao.*)")
    public void pointCutWithin(){
        System.out.println("pointcut");
    }
    
    @Around("pointCutWithin()")
    public void around(ProceedingJoinPoint pij) throws Throwable {
        System.out.println(this.hashCode());
        Object[] args = pij.getArgs();
        if(args!=null && args.length > 0){
            args[0] += "  world";
        }
        System.out.println("AroundBefore");
        pij.proceed(args);
        System.out.println("AroundAfter");
    }
}
  • 测试
public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        Dao dao = (Dao) annotationConfigApplicationContext.getBean("indexDao");
        Dao dao1 = (Dao) annotationConfigApplicationContext.getBean("indexDao");
        System.out.println("dao hashcode " + dao.hashCode());
        System.out.println("dao1 hashcode " + dao1.hashCode());
        dao.query("hello");
        dao1.query();
    }

在这里插入图片描述

  1. 原型为多类型,切面为单类型,原型hascode不同,但是切面的hashcode都相同
    修改

在这里插入图片描述
在这里插入图片描述

AOP是一种思想
AspectJ是AOP的一种的方法
spring也可以实现AOP。
Spring借助了AspectJ中的语法

Enabling @AspectJ Support with Java Configuration

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)

各种连接点joinPoint的意义:

1. execution

用于匹配方法执行 join points连接点,最小粒度方法,在aop中主要使用。

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
这里问号表示当前项可以有也可以没有,其中各项的语义如下
modifiers-pattern:方法的可见性,如publicprotected;
ret-type-pattern:方法的返回值类型,如intvoid等;
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包及其子包中任意的方法
关于这个表达式的详细写法,可以脑补也可以参考官网很容易的,可以作为一个看spring官网文档的入门,打破你害怕看官方文档的心理,其实你会发觉官方文档也是很容易的
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的信息,并且在Spring中,大部分需要使用AOP的业务场景也只需要达到方法级别即可,因而execution表达式的使用是最为广泛的

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)")//接受一个参数,并且传递的参数的运行时类型具有@Classified
 @Pointcut("args(java.lang.Integer,java.lang.String)")  //表示参数方法前面一个必须是Integer类型,后一个必须是String类型

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

Spring底层用的是接口。

Spring AOP defaults to using standard JDK dynamic proxies for AOP proxies. This enables any interface (or set of interfaces) to be proxied.

public class test {
    public static void main(String[] args) throws IOException {

        Class<?>[] interfaces = {Dao.class};
        byte[] bytes = ProxyGenerator.generateProxyClass("LubanAA", interfaces);
        File file = new File("C:\\Users\\50339\\Documents\\Test.class");
        FileOutputStream fw = new FileOutputStream(file);
        fw.write(bytes);
        fw.flush();
        fw.close();

    }
}

JDK代理是继承了Proxy实现了了Dao接口,而cglib是继承了父类。
在这里插入图片描述

  • 代理
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.luban")
public class SpringConfig {
}

@EnableAspectJAutoProxy(proxyTargetClass = true)这里面的true代表的是cglib代理,继承父类,如果不写或者false(默认就是JDK动态代理),就是继承Proxy,实现接口。

如果为proxyTargetClass = false(JDK动态代理)

public class test {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        
        Dao dao =  annotationConfigApplicationContext.getBean(IndexDao.class);

        System.out.println(dao instanceof IndexDao);
    }
}

结果就会报错:IOC容器中没有这个类
在这里插入图片描述

		Dao dao = (Dao) annotationConfigApplicationContext.getBean("indexDao");

        System.out.println(dao instanceof IndexDao);

结果为false.
System.out.println(dao instanceof Proxy); 结果为false
System.out.println(dao instanceof Dao);结果为true

如果为proxyTargetClass = true(cglib动态代理)

public class test {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        
        Dao dao =  annotationConfigApplicationContext.getBean(IndexDao.class);

        System.out.println(dao instanceof IndexDao);
    }
}

结果为: true 表示容器中有这个类

		Dao dao = (Dao) annotationConfigApplicationContext.getBean("indexDao");

        System.out.println(dao instanceof IndexDao);

结果为true.
System.out.println(dao instanceof Proxy); 结果为true
System.out.println(dao instanceof Proxy);结果为true

5. target 指向接口和子类(this表示代理对象,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作用

6. @annotation

这个很简单........
作用方法级别

上述所有表达式都有@ 比如@Target(里面是一个注解类xx,表示所有加了xx注解的类,和包名无关)

注意:上述所有的表达式可以混合使用,|| && !

@Pointcut("@annotation(com.chenss.anno.Chenss)")//匹配带有com.chenss.anno.Chenss注解的方法
@Retention(RetentionPolicy.RUNTIME)
public @interface LuBan {
}
@Repository
public class IndexDao {
    @LuBan
    public void query(){
        System.out.println("query");
    }
}
@Component   //申明是一个Bean
@Aspect
public class LubanAspectj {
    @Pointcut("@annotation(com.luban.anno.LuBan)")
    public void pointCutAnnotation(){
        System.out.println("annotation");
    }

    @Before("pointCutAnnotation()")
    public void before(){
        System.out.println("before");
    }
}
  1. bean
@Pointcut("bean(dao1)")//名称为dao1的bean上的任意方法
@Pointcut("bean(dao*)")
@Component   //申明是一个Bean
@Aspect
public class LubanAspectj {

    @Pointcut("execution(* com.luban.dao.*.*(..))")
    public void pointCutExecution(){
        System.out.println("pointcut");
    }

    @Pointcut("within(com.luban.dao.*)")
    public void pointCutWithin(){
        System.out.println("pointcut");
    }

    @Pointcut("args(java.lang.Integer,java.lang.String)")
    public void pointCutArgs(){
        System.out.println("pointcut");
    }

    @Before("pointCutWithin()&&!pointCutArgs()")
    public void before(){
        System.out.println("before");
    }
}

@Around环绕通知

@Component   //申明是一个Bean
@Aspect
public class LubanAspectj {

    @Pointcut("execution(* com.luban.dao.*.*(..))")
    public void pointCutExecution(){
        System.out.println("pointcut");
    }

    @Pointcut("within(com.luban.dao.*)")
    public void pointCutWithin(){
        System.out.println("pointcut");
    }

    @Pointcut("args(java.lang.Integer,java.lang.String)")
    public void pointCutArgs(){
        System.out.println("pointcut");
    }

    @Pointcut("@annotation(com.luban.anno.LuBan)")
    public void pointCutAnnotation(){
        System.out.println("annotation");
    }

    @Pointcut("target(com.luban.dao.IndexDao)")
    public void pointCutAround(){
        
    }

//    @Before("pointCutExecution()")
//    public void before(JoinPoint jp){
//        System.out.println("before");
//        System.out.println(jp.getThis());
//        System.out.println(jp.getTarget());
//    }
//
//    @After("pointCutExecution()")
//    public void after(){
//        System.out.println("after");
//    }

    @Around("pointCutAround()")
    public void around(ProceedingJoinPoint pij) throws Throwable {
        System.out.println("AroundBefore");
        pij.proceed();
        System.out.println("AroundAfter");
    }
}

修改参数

@Around("pointCutAround()")
    public void around(ProceedingJoinPoint pij) throws Throwable {
        Object[] args = pij.getArgs();
        if(args!=null){
            args[0] += "  world";
        }
        System.out.println("AroundBefore");
        pij.proceed(args);
        System.out.println("AroundAfter");
    }

Introductions

在这里插入图片描述

@Repository("orderDao")
public class OrderDao  {
}

在dao下有一个类,什么方法都没有

  • Aspectj配置
@Component   
@Aspect
public class LubanAspectj {

    @DeclareParents(value="com.luban.dao.*", defaultImpl= IndexDao.class)
    public static Dao dao;

}

  • 测试类获取orderDao,可以调用方法
Dao dao = (Dao) annotationConfigApplicationContext.getBean("orderDao");
dao.query("hello");

这说明在dao包下的类中,默认实现是IndexDao.class类。

用XML配置Aop应用

在这里插入图片描述

  • dao
public interface Dao {
    void query();
}
  • IndexDao
public class IndexDao implements Dao{
    @Override
    public void query() {
        System.out.println("query");
    }
}
  • XMLAopBean
public class XMLAopBean {

    public void before(){
        System.out.println("xml before");
    }
}
  • Test
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        Dao dao = (Dao) classPathXmlApplicationContext.getBean("dao");
        dao.query();

    }
}
  • XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config></context:annotation-config>
    <context:component-scan base-package="com"></context:component-scan>

    <bean id="xmlBean" class="com.luban.dao.XMLAopBean"></bean>

    <bean id="dao" class="com.luban.dao.IndexDao"></bean>


    <aop:config>
        <aop:pointcut id="allDao" expression="execution(* com.luban.dao.*.*(..))"/>

        <aop:aspect id="aspect" ref="xmlBean">
            <aop:before
                    pointcut-ref="allDao"
                    method="before"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

总结:

nameXMLAnnotation和java Config
开启注解<context:annotation-config></context:annotation-config>@Configuration
开启扫描<context:component-scan base-package="com"></context:component-scan>@ComponentScan
添加Bean<bean id="xmlBean" class="com.luban.dao.XMLAopBean"></bean>@Compoenet("xmlBean")(注解配置) 或者 用Java config配置类上添加方法return new XMLAopBean();上添加@Bean
相互调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值