当前位置: 首页 > news >正文

Spring之AOP简单讲解

目录

一:基本概念

二:案例:模拟AOP的基础代码

三:AOP相关概念

四:AOP入门案例思路分析

五:AOP工作流程

六:AOP核心概念

七:AOP切入点表达式

八:xml方式AOP快速入门

九:案例:测量业务层接口万次执行效率

JoinPoint对象

 十:案例:百度网盘密码数据兼容处理

语法形式不同:

可配置的切面数量不同:

使用场景不同:

xml方式AOP原理剖析

注解方式AOP基本使用

注解方式AOP原理剖析

十一:AOP总结


一:基本概念

               AOP, Aspect Oriented Programming, 面向切面编程,一种编程范式,指导开发者如何组织程序结构, 是对面向对象编程OOP的升华。OOP(Object Oriented Programming)是纵向对一个事物的抽象, 一个对象包括静态的属性信息, 包括动态的方法信息等。而AOP是横向的对不同事物的抽象, 属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

        作用:在不惊动原始设计的基础上为其进行功能增强

        Spring理念:无入侵式/无侵入式

 

 

 

 

 

二:案例:模拟AOP的基础代码

               其实在之前学习BeanPostProcessor时, 在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强, 实际存储到单例池singleObjects中的不是当前目标对象本身, 而是当前目标对象的代理对象Proxy, 这样在调用目标对象方法时, 实际调用的是代理对象Proxy的同名方法, 起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.tangyuan.service.impl包下的任何类的任何方法进行增强

//自定义增强类
  public class My Advice{
        public void beforeAdvice() {
           System.out.println("beforeAdvice...") ;
        }

        public void afterAdvice() {
           System.out.println("afterAdvice...") ;
        }

}

1.创建service类

public interface UserService {
     void show1();
    
     void show2();

}  

2.实现service的实现类

public class UserServiceImpl implements UserService { 
  @Override
    public void show1() {  
          System.out.println(show1.....);
    }

      @Override
    public void show2() {  
          System.out.println(show2.....);
    }


}

ps:以上目标对象,目标类已经准备完成

3.创建增强类,内部提供增强方法

public class MyAdvice{
      //前置增强方法
      public void beforeAdvice(){
            System.out.println(前置的增强.....);
      }
    
     public void afterAdvice(){
            System.out.println(后置的增强.....);
      }    
    
}

目的:在执行show1()方法的同时,也要将before和after的方法执行

4.将UserServiceImpl类和MyAdvic类配置到xml文件Spring容器中

<bean id="userService" class="com.tangyuan.service.impl.UserServiceImpl"></bean>
<bean id="myAdvice" class="com.tangyuan.advice.MyAdvice"></bean>

5.BeanProxy对象创建

 

public class MockAopBeanPostProcessor implements BeanPostProcessor,ApplicationContextAware{
    //成员变量
    private ApplicationContext applicationContext;    
    
      @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       //目的:对UserServiceImpl中的show1和show2方法进行增强,增强方法存在于MyAdvice类中
        //问题:1.筛选Service,impl包下的所有的类的所有方法都可以进行增强----解决方案if-else
        //2.MyAdvice怎么获取到?解决方案:从Spring容器中获得MyAdvice
        if(bean.getClass().getPackage().getName().equals("com.tangyuan.service.impl")){
            //生成当前bean的Proxy对象
          Object beanProxy=Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (Object proxy,Method  method,Object[] args) -> {
                     MyAdvice myAdvice=applicationContext.getBean(MyAdvice.class);
                    //执行增强对象的before方法
                     myAdvice.beforeAdvice();
                    //执行目标对象的目标方法
                    Object result = method.invoke(bean, args);
                    //执行增强对象的after法
                     myAdvice.afterAdvice();
                    return result;
                }
        );
            
            return beanProxy;  
        }        
        return bean;
    }
    
     @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
         this.applicationContext=applicationContext;
    }
    
    

}

6.将 MockAopBeanPostProcessor类配置到xml文件Spring容器中

7.测试

  ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
   IUserService userService = applicationContext.getBean(UserService.class);
        userService.show1();

三:AOP相关概念

 

切入点范围小,连接点范围大,切入点一定在连接点中

 

四:AOP入门案例思路分析

 

目的:在接口执行前输出当前系统时间

开发模式:xml or 注解

思路分析:

1.导入坐标(pom.xml)

 <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
  </dependencies>

 spring-context坐标依赖spring-aop坐标:

2.制作连接点方法(原始操作,Dao接口与实现类)  

public interface BookDao {
    public void save();
    public void update();
}
import com.tangyuan.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao {

    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }

    public void update(){
        System.out.println("book dao update ...");
    }

3.制作共性功能(通知类与通知)

     创建一个类,用来存储共性功能

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//通知类必须配置成Spring管理的bean
@Component
//设置当前类为切面类类
@Aspect
public class MyAdvice {
    //4.定义切入点
    //设置切入点,要求配置在方法上方
    @Pointcut("execution(void com.tangyuan.dao.BookDao.update())")
    private void pt(){}

     //5.绑定切入点与通知关系(切面)
    //设置在切入点pt()的前面运行当前操作(前置通知)
     @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

 4.定义切入点

     切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑

5.绑定切入点与通知关系(切面),并指定通知添加到原始接点的具体执行位置

6.定义通知类受Spring容器管理,并定义当前类为切面类

7.在配置类中进行相关属性的配置,开启Spring对AOP注解驱动支持

五:AOP工作流程

1.Spring容器启动

2.读取所有切面配置中的切入点

@Component
@Aspect
public class MyAdvice{
@Pointcut("execution(void com.tangyuan.dao.BookDao.save())")
private void ptx() {}

@Pointcut("execution(void com.tangyuan.dao.BookDao.update())")
private void pt() {}

@Before("pt() ")
public void method() {
System.out.println(System.currentTimeMiLLis() ) ;
}
}

3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

匹配失败,创建对象

匹配成功,创建原始对象(目标对象)的代理对象

4.获取bean执行方法

获取bean,调用方法并执行,完成操作

获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

六:AOP核心概念

            目标对象(Target) :原始功能去掉共性功能对应的类产生的对象, 这种对象是无法直接完成最终工作的

             代理(Proxy) :目标对象无法直接完成工作, 需要对其进行功能回填, 通过原始对象的代理对象实现

七:AOP切入点表达式

            切入点:要进行增强的方法

           切入点表达式:要进行增强的方法的描述方式

 

 

八:xml方式AOP快速入门

 

前面我们自己编写的AOP基础代码还是存在一些问题的, 主要如下:

  • 被增强的包名在代码写死了

  • 通知对象的方法在代码中写死了

if("com.tangyuan.service.impl".equals(packageName) ) {
//对Bean进行动态代理, 返回的是Proxy代理对象
Object proxy Bean=Proxy.new Proxy Instance(
bean.getClass() .getClassLoader() ,
bean.getClass() .get Interfaces() ,
(Object proxy,Method method, Object[]  args)->{
myAdvice.beforeAdvice() ; //执行Advice的before方法
Object result=method.invoke(bean, args) ; //执行目标
myAdvice.afterAdvice() ; //执行Advice的after方法
return result;
});
//返回代理对象
return proxyBean;
}
  return bean;
}

通过配置文件的方式去解决上述问题

  • 配置哪些包、哪些类、哪些方法需要被增强

  • 配置目标方法要被哪些通知方法所增强,在目标方法执行之前还是之后执行增强

    配置方式的设计、配置文件(注解) 的解析工作, Spring已经帮我们封装好了

xml方式配置AOP的步骤:

1、导入AOP相关坐标;

2、准备目标类、准备增强类, 并配置给Spring管理;

3、配置切点表达式(哪些方法被增强);

4、配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)。

1.导入AOP相关坐标;

 <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>

2.目标和通知

<!--配置目标类-->
<bean id="userService" class="com.tangyuan.service.impl.UserServiceImpl"></bean>
<!--配置通知类-->
<bean id="myAdvice" class="com.tangyuan.advice.MyAdvice"></bean>

3.配置aop的命名空间

xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd

4.配置切点表达式

<!--aop配置-->
<aop:config>
<!--配置切点表达式,目的是要指定哪些方法被增强-->
<aop:pointcut id="myPointcut" expression="execution(void com.tangyuan.service.impl.UserServiceImpl.show1() ) "/>
    <!--配置织入,目的是要执行哪些切点与那些通知进行结合-->
<aop:aspect ref="myAdvice">
      <aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
</aop:aspect>
    
</aop:config>

xml配置AOP的方式还是比较简单的, 下面看一下AOP详细配置的细节:

  • 切点表达式的配置方式

  • 切点表达式的配置语法

  • 通知的类型

  • AOP的配置的两种方式

ps:

1.切点表达式可以配置在外部,可以使用pointcut-ref来引用切点表达式的id

2.pointcut="execution(void com.tangyuan.service.impl.UserServiceImpl.show1() )"

3.切点表达式可以配置多个

切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:

execution([访问修饰符] 返回值类型包名.类名.方法名(参数) )

其中,

  • 访问修饰符可以省略不写;

  • 返回值类型、某一级包名、类名、方法名可以使用*表示任意;

  • 包名与类名之间使用单点.表示该包下的类,使用双点..表示该包及其子包下的类;

  • 参数列表可以使用两个点..表示任意参数。

切点表达式举几个例子方便理解

//表示访问修饰符为public、无返回值、在com.tangyuan.aop包下的TargetImpl类的无参方法show
execution(public void com.tangyuan.aop.TargetImpl.show() )
//表述com.tangyuan.aop包下的Target Impl类的任意方法
execution(*com.tangyuan.aop.Target Impl.*(..) )
//表示com.tangyuan.aop包下的任意类的任意方法
execution(*com.tangyuan.aop.*.*(..) )
//表示com.tangyuan.aop包及其子包下的任意类的任意方法
execution(*com.tangyuan.aop..*.*(..) )
//表示任意包中的任意类的任意方法
execution(**..*.*(..) )

       AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

public void beforeAdvice() {
   System.out.println("前置的增强....") ;
}

public void after Returning Advice() {
   System.out.println("后置的增强....") ;
}


<!--环绕通知方法-->
public Object around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
      System.out.println("环绕前的增强....") ;
      Object res=proceedingJoinPoint.proceed() ; //执行目标方法
      System.out.println("环绕后的增强....") ;
      return res;
}


public void afterThrowingAdvice() {
      System.out.println("异常抛出通知...报异常才执行") ;
  }

public void afterAdvice() {
     System.out.println("最终的增强....") ;
}




<!--前置通知-->
<aop:before method="beforeAdvice" pointout-ref="myPointcut2"/>
<!--后置通知-->
<aop:after-returning method="afterReturningAdvice" pointcut-ref-"myPointcut2”
<!--坏绕通知-->
<aop:around method="around" pointcut-ref="myPointcut2"/>
<!--异常抛出通知-->
<aop:after-throwing method="afterThrowingAdvice" pointcut-ref="myPointcut2"/>
<!--最终通知-->
<aop:after method="afterAdvice" pointcut-ref="myPointcut2"/>

 

 

 

 

 

 

九:案例:测量业务层接口万次执行效率

 

1.dao接口  

import com.tangyuan.domain.Account;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface AccountDao {

    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    @Delete("delete from tbl_account where id = #{id} ")
    void delete(Integer id);

    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id = #{id} ")
    Account findById(Integer id);
}

2.service接口及实现类

import com.tangyuan.domain.Account;

import java.util.List;

public interface AccountService {

    void save(Account account);

    void delete(Integer id);

    void update(Account account);

    List<Account> findAll();

    Account findById(Integer id);
    }
import com.tangyuan.dao.AccountDao;
import com.tangyuan.domain.Account;
import com.tangyuan.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void save(Account account) {
        accountDao.save(account);
    }

    public void update(Account account){
        accountDao.update(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

3.实体类

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

4.配置类

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;


public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.itheima.dao");
        return msc;
    }
}
import org.springframework.context.annotation.*;​
@Configuration@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableAspectJAutoProxy
public class SpringConfig {}

5.切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class ProjectAdvice {
    //匹配业务层的所有方法
    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    private void servicePt(){}

    //设置环绕通知,在原始操作的运行前后记录执行时间
    @Around("ProjectAdvice.servicePt()")
    public void runSpeed(ProceedingJoinPoint pjp) throws Throwable {
        //获取执行的签名对象
        Signature signature = pjp.getSignature();
        //通过签名获取执行类型(接口名)
        String className = signature.getDeclaringTypeName();//com.tangyuan.service.AccountService
        //通过签名获取执行操作名称(方法名)
        String methodName = signature.getName();//findById

        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
           pjp.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    }

当前测试的接口效率仅仅是一个理论值,并不是一次完整的执行过程

通知方法在被调用时, Spring可以为其传递一些必要的参数

参数类型作用
JoinPoint连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息
ProceedingJoinPointJoinPoint子类对象, 主要是在环绕通知中执行proceed() , 进而执行目标方法
Throwable异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称

JoinPoint对象

       描述了连接点方法的运行状态,可以获取到原始方法的调用参数

public void 通知方法名称(JoinPoint joinPoint) {
//获得目标方法的参数
System.out.println(joinPoint.getArgs() ) ;
//获得目标对象
System.out.println(joinPoint.getTarget() ) ;
//获得精确的切点表达式信息
System.out.println(joinPoint.getStaticPart() ) ;
}

ProceedingJoinPoint对象是JoinPoint的子类

 public Object around(ProceedingJoinPoint joinPoint) throws Throwable{   System.out.println(joinPoint.getArgs() ) ; //获得目标方法的参数   System.out.println(joinPoint.getTarget() ) ; //获得目标对象   System.out.println(joinPoint.getStaticPart() ) ; //获得精确的切点表达    Objectresult=joinPoint.proceed() ; //执行目标方法       return result; //返回目标方法返回值}

Throwable对象

public void afterThrowing(JoinPoint joinPoint, Throwable th) {
     //获得异常信息
    System.out.println("异常对象是:"+th+"异常信息是:"+th.get Message() )
}
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"  throwing="th"/>

 十:案例:百度网盘密码数据兼容处理

1.dao接口及实现类  

public interface ResourcesDao {

    boolean readResources(String url, String password);
}
@Repository
public class ResourcesDaoImpl implements ResourcesDao {
    public boolean readResources(String url, String password) {
        System.out.println(password.length());
        //模拟校验
        return password.equals("root");
    }
}

2.service接口及实现类

public interface ResourcesService {
    public boolean openURL(String url ,String password);

}
import com.tangyuan.dao.ResourcesDao;
import com.tangyaun.service.ResourcesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ResourcesServiceImpl implements ResourcesService {
    @Autowired
    private ResourcesDao resourcesDao;

    public boolean openURL(String url, String password) {
        return resourcesDao.readResources(url,password);
    }
}

3.配置类

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

@Configuration
@ComponentScan("com.tangyuan")
@EnableAspectJAutoProxy
public class SpringConfig {
}

4.切面类

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

@Component
@Aspect
public class DataAdvice {
    @Pointcut("execution(boolean com.tangyuan.service.*Service.*(*,*))")
    private void servicePt(){}

    @Around("DataAdvice.servicePt()")
    public Object trimStr(ProceedingJoinPoint pjp) throws Throwable {
        //获取参数
        Object[] args = pjp.getArgs();
        //遍历参数
        for (int i = 0; i < args.length; i++) {
            //判断参数是不是字符串
            if(args[i].getClass().equals(String.class)){
                args[i] = args[i].toString().trim();
            }
        }
        Object ret = pjp.proceed(args);
        return ret;
    }
}

5.测试

import com.tangyuan.config.SpringConfig;
import com.tangyuan.service.ResourcesService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        ResourcesService resourcesService = ctx.getBean(ResourcesService.class);
        boolean flag = resourcesService.openURL("http://pan.baidu.com/haha", "root ");
        System.out.println(flag);
    }
}

AOP配置的两种语法形式

AOP的xml有两种配置方式, 如下:

  • 使用<advisor>配置切面

  • 使用<aspect>配置切面

    Spring定义了一个Advice接口, 实现了该接口的类都可以作为通知类出现

 public interface Advice{ }

            advisor需要的通知类需要实现Advice的子功能接口, 例如:MethodBeforeAdvice、AfterReturningAdvice等, 是通过实现的接口去确定具备哪些通知增强的, 见代码演示


<!--通知规范类-->
public class MyAdvice2 implements MethodBeforeAdvice, AfterReturningAdvice{
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
      System.out.println("前置通知.."}
}


@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o) throws Throwable
    System.out.println("后置通知...........") ;
       }
 }
public class MyAdvice3 implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable{
    System.out.println("环绕前...........") ;
//执行目标方法
Object res=method Invocation.getMethod() .invoke(methodInvocation.getThis(),methodInvocation.getArguments() ) ;
      System.out.println("环绕后...........") ;
return res;
}

AOP配置的两种语法形式不同点

语法形式不同:

  • advisor是通过实现接口来确认通知的类型,如MyAdvice2

  • aspect是通过配置确认通知的类型, 更加灵活,如

  • <aop:aspect ref="myadvice">
    
    <aop:after method="afterAdvice" pointcut-ref="myPointcut2"/>
    
    /aop:aspect

可配置的切面数量不同:

  • 一个advisor只能配置一个固定通知和一个切点表达式

  • 一个aspect可以配置多个通知和多个切点表达式任意组合

使用场景不同:

  • 允许随意搭配情况下可以使用aspect进行配置

  • 如果通知类型单一、切面单一的情况下可以使用advisor进行配置

  • 在通知类型已经固定, 不用人为指定通知类型时, 可以使用advisor进行配置, 例如后面要学习的Spring事务控制的配置

  • xml方式AOP原理剖析

 

 

 

 

 

JDK的动态代理代码, 之前已经写过了, 下面看一下Cglib基于超类的动态代理  

 

Target target=new Target() ; //目标对象
Advices advices三new Advices() ; //通知对象
Enhancer enhancer=new Enhancer() ; //增强器对象
enhancer.setSuperclass(Target.class) ; //增强器设置父类
//增强器设置回调
enhancer.set Callback( (MethodInterceptor) (o, method,objects,method Proxy)一>{

advices.before() ;
Object result=method.invoke(target, objects) ;
advices.afterReturning() ;
return result;

});
//创建代理对象
Target targetProxy=(Target) enhancer.create() ;
//测试
String result=targetProxy.show("haohao") ;

  • 注解方式AOP基本使用

         Spring的AOP也提供了注解方式配置, 使用相应的注解替代之前的xml配置, xml配置AOP时, 我们主要配置了三 部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面) , 如下

<!--配置目标-->
<bean id="target"class="com.tangyuan.aop.Target Impl"></bean>
<!--配置通知-->
<bean id="advices"class="com.tangyuan.aop.Advices"></bean>
<!--配置aop-->
<aop:config proxy-target-class="true">
<aop:aspect ref="advices">
    <aop:around method="around"pointcut="execution(*com.tangyuan.aop.*.*(..))"/>
</aop:aspect>
</aop:config>

1.接口service类

@Service("userService")
public class UserServiceImpl implements UserService{ }

2.增强类

//增强类,内部提供增强方法
@Component
@Aspect
public class MyAdvice{
//<aop:beforemethod="beforeAdvice"pointcut="execution(*com.tangyuan.service.impl.*.*(..))"/>
@Before("execution(*com.tangyuan.service.impl.*.*(..) ) ")
public void beforeAdvice(JoinPoint joinPoint) {
    System.out.println("当前目标对象是:"+joinPoint.getTarget() ) ;
    System.out.println("表达式:"+joinPoint.getStaticPart() ) ;
    System.out.println("前置的增强....") ;
}
}

3.在xml文件中进行配置

<!--组件扫描-->
<context:component-scan base-package="com.tangyuan"/>

<!--使用注解配置AOP, 需要开启AOP自动代理-->
<aop:aspectj-autoproxy/>

 

各种注解方式通知类型
//前置通知
@Before("execution(*com.tangyuan.aop.*.*(..) ) ")
public void before(JoinPoint joinPoint) {}
//后置通知
@AfterReturning("execution(*com.tangyuan.aop.*.*(..) ) ")
public void AfterReturning(JoinPoint joinPoint) {}
//环绕通知
@Around("execution(*com.tangyuan.aop.*.*(..) ) ")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{}
//异常通知
@AfterThrowing("execution(*com.tangyuan.aop.*.*(..) ) ")
public void AfterThrowing(Join Point join Point) {}
//最终通知
@After("execution(*com.tangyuan.aop.*.*(..) ) ")
public void After(JoinPoint joinPoint) {}

//切点表达式的抽取
@Pointcut("execution(*com.tangyuan.service.impl.*.*(..))")
public void myPointcut() {}

使用:
@Around(“类名.myPointcut()”)
@Confiquration
@ComponentScan("com.tangyuan")//<context:component-scan base-package="com.tangyuan"/>
@EnableAspectJAutoProxy //<aop:aspectj-autoproxy/>
public class Spring Config{ }

  • 注解方式AOP原理剖析

 

 

 

 

十一:AOP总结

 

 

 

 

相关文章:

  • 解决Vue3中使用setup如何定义组件的name属性
  • antd 类组件swiper中的指示器和ref
  • 自主异常检测算法(Matlab代码实现)
  • java中的位运算符
  • 二十、解释器模式 ( Interpreter Pattern )
  • SpringMVC视图视图控制器
  • 怎么把两个PDF合并成一个?这几种操作轻松合并
  • 微信小程序——视图与逻辑,页面导航(导航到 tabBar 页面,导航到非 tabBar 页面)
  • 系分 - 案例分析 - 项目管理
  • 产品---竞品分析
  • 第九届蓝桥杯省赛 C++ A组 - 付账问题
  • 从汇编的角度了解C++原理——类的储存结构和函数调用
  • 双向bfs-字串变换
  • 软考报名有没有学历要求?2023年软考报名条件分享
  • linux下调节GPU的功率限制
  • 冥想第六百七十五天
  • 牛客竞赛每日俩题 - 动态规划4
  • python 列表生成式
  • MongoDB面试题整理-四年经验
  • 机器学习笔记之深度玻尔兹曼机(一)玻尔兹曼机系列整体介绍