Spring的简单使用(2)
一:AOP框架:
版本一:业务和切面耦合在一起,没有拆分
public class BookServiceImpl {
    public void buy(){
        System.out.println("事务开启");
        System.out.println("买书业务");
        System.out.println("事务关闭");
    }
}
版本二:使用子类代理的方式拆分业务和切面
public class BookService {
    public void buy(){
        System.out.println("买书");
    }
}
public class SubBookService extends BookService{
    @Override
    public void buy() {
        System.out.println("事务开启");
        super.buy();
        System.out.println("事务关闭");
    }
}
版本三:使用静态代理拆分业务和切面,业务和业务接口已拆分,此时切面耦合在业务中
public interface Service {
    void buy();
}
public class BookServiceImpl implements Service {
    public void buy() {
        System.out.println("买书");
    }
}
public class PenServiceImpl implements Service {
    public void buy() {
        System.out.println("买笔");
    }
}
public class Agent implements Service {
    private Service target;
    public Agent(Service target) {
        this.target=target;
    }
    public void buy() {
        System.out.println("事务开启");
        target.buy();
        System.out.println("十五关闭");
    }
}
版本四:使用静态代理拆分业务和业务接口,切面和切面接口
业务接口:
public interface Service {
    void buy();
}
业务:
public class BookServiceImpl implements Service {
    public void buy() {
        System.out.println("买书");
    }
}
public class PenServiceImpl implements Service {
    public void buy() {
        System.out.println("买笔");
    }
}
切面接口:
public interface Aop {
    default void before() {
    }
    default void after() {
    }
  
}
切面:
public class LogAop implements Aop{
    @Override
    public void before() {
        System.out.println("在方法前使用日志");
    }
}
public class TranstionAop implements Aop{
    @Override
    public void before() {
        System.out.println("事务提交前");
    }
    @Override
    public void after() {
        System.out.println("事务提交后");
    }
}
代理:
public class Agent implements Service,Aop {
    private Service target;
    private Aop aop;
    public Agent(Service target,Aop aop) {
        this.target=target;
        this.aop=aop;
    }
    public void buy() {
        aop.before();
        target.buy();
        aop.after();
    }
}
版本五:使用动态代理完成版本四的优化
优化代理:使用动态代理:
public class Agent {
   public static Object getAgent(Service target,Aop aop){
       return Proxy.newProxyInstance(
               //类加载器
               target.getClass().getClassLoader(),
               //目标对象实现的所有接口
               target.getClass().getInterfaces(),
               //代理功能实现
               new InvocationHandler() {
                   @Override
                   public Object invoke(
                           //生成的代理对象
                           Object proxy,
                           //被调用的方法
                           Method method,
                           //目标方法的参数
                           Object[] args) throws Throwable {
                       //切面
                       aop.before();
                       //业务
                       Object invoke = method.invoke(target, args);
                       //切面
                       aop.after();
                       //切面
                       return invoke;
                   }
               });
   }
}
Aop常用的术语:

三:AspectJ框架:是一个面向切面的框架,扩展了java语言,提供了强大的切面实现,要比spring原生的aop框架好用的多
有常见的四种类型:前置通知@Before,后置通知:@AfterReturning,环绕通知:@Around,最终通知:@After,定义切入点:@Pointcut(了解)

1: AspectJ的前置通知@Before
在目标方法执行前切入切面功能,在切面方法中不可以获得目标方法的返回值,只能得到目标方法的签名
步骤:1创建业务接口2创建业务实现3创建切面类4在applicationContext.xml文件中进行切面绑定
导入的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
业务接口:
public interface BookService {
    String buy(String name,String adress);
    String show(String name,int age);
}
业务实现:
@Component
public class BookServiceImpl implements BookService{
@Override
public String buy(String name, String adress) {
System.out.println("去学校买书");
return "aaaa";
}
@Override
public String show(String name, int age) {
System.out.println("show方法被调用");
return "aaaa";
}
}
切面类:
@Component
@Aspect//让aspect找到这个切面类
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义 4 方法没有参数,如果有也只能是JoinPoint类型
//业务方法 String buy(String name,String adress);
@Before(value = "execution( String com.ztb.aop1.BookServiceImpl.*(..))")
public void mybefore(){
System.out.println("切面方法的前置通知功能实现");
}
}
applicationContex.xml:
<context:component-scan base-package="com.ztb.aop1"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
测试:
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookserviceImpl =(BookService) context.getBean("bookServiceImpl");
bookserviceImpl.show("dd",1);
}
AspectJ切换JDK动态代理和CGlig动态代理:
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 只能用接口类型
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 设置为CGLib子类代理,可以用接口和实现类来接
注意:使用接口来接,永远不会出错
前置通知方法JointPoint:
@Before(value = "execution( String com.ztb.aop1.BookServiceImpl.*(..))")
public void mybefore(JoinPoint jp){
System.out.println("切面方法的前置通知功能实现");
System.out.println(Arrays.toString(jp.getArgs()));//得到目标方法的参数
System.out.println(jp.getSignature());//得到目标方法的签名
}
2:后置通知:
@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){
System.out.println("切面方法的后置通知功能实现");
后置通知无法改变目标方法返回值例子:
@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){
System.out.println("切面方法的后置通知功能实现");
if(obj!=null){
if(obj instanceof String){
obj = obj.toString().toUpperCase();
System.out.println(obj);
}
}
}
}
后置通知无法可以目标方法返回值例子:
@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){
System.out.println("切面方法的后置通知功能实现");
if(obj!=null){
if(obj instanceof String){
obj = obj.toString().toUpperCase();
System.out.println(obj);
}
if(obj instanceof Student){
((Student) obj).setName("李四");
System.out.println(((Student) obj).getName());
}
}
}
}
3:环绕通知:
@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法有返回值,此返回值就是目标方法的返回值 3方法名称自定义
// 4 方法有参数,此参数就是目标方法
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@Around(value = "execution(* com.ztb.aop1.*.*(..))")
public Object myaround(ProceedingJoinPoint joinPoint) throws Throwable {
//前切功能
System.out.println("环绕通知中的前置功能");
//目标方法调用
Object object = joinPoint.proceed(joinPoint.getArgs());
//后切功能
System.out.println("环绕通知中的后置功能");
return object.toString().toUpperCase();//环绕通知可以更改目标方法的返回值
}
}
4:最终通知:
无论目标方法是否正常执行,最终通知的代码都会被执行
@After(value = "mycut()")
public void myaround() {
System.out.println("最终通知");
}
@Pointcut(value = "execution(* com.ztb.aop1.*.*(..))")
public void mycut(){}
可以为一个目标方法添加各种通知:则执行顺序为:

为切入点表达式起别名:使用pointcut
@After(value = "mycut()")
public void myaround() {
System.out.println("最终通知");
}
@Pointcut(value = "execution(* com.ztb.aop1.*.*(..))")
public void mycut(){}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号