Spring之AOP简单讲解

news2024/11/28 14:50:36

目录

一:基本概念

二:案例:模拟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总结

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态内存分配/管理

目录 1、为什么要有动态内存分配 2、动态内存函数介绍 1、malloc 2、free 3、calloc ​编辑 4、realloc 3、动态内存常见的错误 4、动态内存开辟相关好题 5、c/c程序内存开辟示意图 int a, int arr[10] 是固定地向内存申请连续的一块空间&#xff0c;但不能变长或变…

快速理解机器学习、深度学习与自然语言处理

这篇文章对机器学习、深度学习、自然语言处理进行了简单的介绍&#xff0c;适合快速学习NLP与AI、ML和DL的关系。 机器学习、深度学习、自然语言处理的关系 机器学习、深度学习、自然语言处理的关系如图所示 1 机器学习&#xff08;Machine Learning&#xff09; 机器学习…

python中的设计模式:单例模式、工厂模式

目录 一.设计模式 二.单例模式 二.工厂模式 优点: 总结 一.设计模式 设计模式是一种编程套路&#xff0c;可以极大的方便程序的开发。 最常见、最经典的设计模式,就是我们所学习的面向对象了。 除了面向对象外,在编程中也有很多既定的套路可以方便开发&#xff0c;我们称…

《Linux Shell脚本攻略》学习笔记-第三章

3.1 简介 Unix为所有的设备和系统功能提供了文件形式的借口。可以通过这些特殊文件直接访问设备以及系统功能。 3.2 生成任意大小的文件 创建特定大小的文件最简单的方法就是利用dd命令。dd命令会克隆给定的输出内容&#xff0c;然后将一模一样的一份副本写入输出。 if表示输入…

个人总结:Mysql知识图谱

使用思维导图工具对mysql使用&#xff0c;进行知识总结。着重说下explanid SELECT识别符。这是SELECT查询序列号。这个不重要,查询序号即为sql语句执行的顺序select_type select类型table 输出的行所用的表&#xff0c;这个参数显而易见&#xff0c;容易理解partitions type 连…

Android系统启动(四) — Launcher 启动过程

1 Launcher 概述 系统启动的最后一步是启动一个应用程序来显示系统中已经安装的应用程序&#xff0c;这个应用程序就叫做 Launcher。Launcher 在启动过程中会请求 PackageManagerService 返回系统中已经安装的应用程序信息&#xff0c;并将这些信息封装成一个快捷图标列表显示…

阿里系-淘宝接口抓取及相关问题

阿里系-淘宝接口抓取 一、安装charlse抓包工具 官方下载地址 安装证书 二、安装xposed hook框架 Xponsed简介 具体安装步骤 三、安装模块 关闭阿里系ssl验证 开启http模式 支持支付宝、淘宝、淘宝直播各个接口抓取 四、效果如下 接下去一段时间更新阿里系相关接口 文章目录 一、…

搞技术的要不要学习财务知识

越是大型的集团或者企业&#xff0c;公司里面设立的部门就越多&#xff0c;也越细化&#xff0c;各部门之间既相互独立管理&#xff0c;又是相互的辅助支持&#xff0c;所以在工作中经常遇到这样的一个问题&#xff0c;就是做技术的要不要学习财务知识。这个问题其实就是把技术…

折半查找算法[二分查找法]算法的实现和解决整数溢出问题~

算法实现的要求&#xff1a; 折半查找法又称为二分查找法&#xff0c;这种方法对待查找的列表有两个要求&#xff1a; 1&#xff1a;必须采用顺序存储结构 2&#xff1a;必须按关键字大小有序排列算法思想&#xff1a; 将表中间位置记录的关键字与查找关键字进行比较&#x…

synchronized实现原理

0. 前言 造成线程安全问题的主要诱因有两点&#xff0c;一是存在共享数据(也称临界资源)&#xff0c;二是存在多个线程共同操作共享数据。因此为了解决线程安全问题&#xff0c;我们可能需要这样一个方案&#xff0c;当存在多个线程操作共享数据时&#xff0c;需要保证同一时刻…

ICV:2022年稀释制冷机全球市场规模达2.11亿美元,2028年有望出现突破点

全球前沿科技咨询机构ICV于2023年初发布了稀释制冷机&#xff08;DR&#xff09;的市场分析报告&#xff0c;ICV在报告中表示&#xff0c;2019-2015稀释制冷机的年均增长率达到8.59%以上&#xff0c;且增长率逐年上升。2022年全球稀释制冷机市场规模将达到2.11亿美元&#xff0…

从Deepmind最新成果DreamerV3启发的通用AI技术分析

一、背景 本文系个人观点&#xff1a;错漏在所难免&#xff0c;仅供参考 北京时间 1 月 12 日&#xff0c;DeepMind 官方推特发文&#xff0c;正式官宣 DreamerV3&#xff0c;这是首个能在游戏「我的世界」(Minecraft) 中不参考人类数据&#xff0c;就能从头开始收集钻石的通…

1.16中断实验

一.异常处理流程 1.异常处理流程 &#xff08;1&#xff09;保存现场&#xff08;CPU自动完成&#xff09; 将CPSR中状态保存到SPSR_<MODE>中 将CPSR寄存器的状态位T&#xff0c;改为ARM状态 根据需要&#xff0c;进制IRQ,FIQ中断&#xff0c;修改C…

java springboot 项目构建报错解决办法

这里总结了一些常见的springboot 项目构建报错及解决办法 错误: 无效的源发行版:17 错误原因 build.gradle 文件中可以通过下面两项来指定项目运行所需的jdk版本 sourceCompatibility:指定编译编译.java文件的jdk版本 targetCompatibility&#xff1a;确保class文件与target…

ARM 看门狗定时器

一、什么是看门狗、有什么用 (1) 看门狗定时器和普通的定时器并无本质区别。定时器可以设定一个时间&#xff0c;在这个时间完成之前定时器不断计时&#xff0c;时间到的时候&#xff0c;定时器会复位 CPU&#xff08;重启系统&#xff09;。 (2 )系统正常工作的时候当然不希望…

feign漫谈

feign的简单使用。 文章目录什么是feign准备工作三.如何使用3.1 定义pom文件3.2 定义配置文件及启动类注解3.3 定义feign接口什么是feign 远程调用框架 准备工作 需要nacos环境&#xff1a; 涉及到feign调用&#xff0c;就没法抛开注册中心&#xff0c;接下来我们使用主流的…

使用Python创建websocket服务端并给出不同客户端的请求

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 使用Python创建websocket服务端&#xff0c;并给出不同客户端的请求 一、 WebSocket是啥 WebSocket 和HTTP一样&#xff0c;也是一种通…

java中的位运算符

在Java语言中&#xff0c;提供了7种位运算符&#xff0c;分别是按位与&#xff08;&&#xff09;、按位或&#xff08;|&#xff09;、按位异或&#xff08;^&#xff09;、取反(~)、左移(<<)、带符号右移(>>)和无符号右移(>>>)。位运算符是对long、i…

怎么把两个PDF合并成一个?这几种操作轻松合并

我们在工作中处理过的文件有很多&#xff0c;有时候为了工作更方便&#xff0c;我们通常需要把两个或是多个文件合并成为一个PDF文件&#xff0c;这样只需要打开这一个文件就可以查看全部内容&#xff0c;那么怎么把两个PDF合并成一个呢&#xff1f;这几种操作都可以轻松合并&a…

Dopamine-PEG-NH2氨基聚乙二醇多巴胺,材料改性用科研试剂

英 文 &#xff1a;NH2-PEG-Dopamine/Dopamine-PEG-NH2 中文&#xff1a;氨基聚乙二醇多巴胺 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;不用于诊治 外观: 固体或粘性液体&#xff0c;取决于分子量 注意事项…