(一)代理模式

代理模式

为什么要学习代理模式?这是就是AOP的底层!【SpringAOP 和SpringMVC】

代理模式的分类:

静态代理:为每一个原始类手动编写一个代理类(java.class)

动态代理

静态代理案例:

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

10.1概念:

通过代理类,为原始类(目标)增加额外的功能

好处:利于原始类(目标)的维护

10.2名词解释

目标类	原始类
指的是业务类(核心功能-----》业务运算DAO调用)
目标方法 原始方法
目标类中的方法,就是目标方法
额外功能(附加功能
日志、事务、性能

10.3代理开发的核心要素

代理类 = 目标类 + 额外功能+ 和原始类实现相同的接口
房东----->public interface UserService(){
			m1;
			m2;
		}
		UserServiceImpl implements UserService(){
			m1;------>业务核心代码
			m2;
		}
		//代理类需要实现与目标类相同的接口
		UserServiceProxy implements UserService(){
			m1;
			m2;
		}

    

10.4 编码

原始类接口,

public interface UserService{
    public void register();
    public boolean login(String name,String password);
}

原始类实现,只专注于核心业务代码

public class UserServiceImpl interface UserService{
    public void register(){
        System.out.println("原始类的注册方法");
    }
    public boolean login(String name,String password){
        System.out.println("原始类的登录");
        return true;
    } 
}

代理类实现

public class UserServiceProxy interface UserService{
    private UserServiceImpl user = newUserServiceImpl();
    
    public void register(){
        System.out.println("代理类的增加的额外功能");
        user.register();
    }
    public boolean login(String name,String password){
        System.out.println("代理中的额外功能如日志、性能、事务");
        return user.login(name,password);     
    } 
}

当增加一个原始类时,需要在增加一个代理类

10.5静态代理存在的问题

静态代理文件数量过多,不利于项目管理

UserServiceImpl		UserServiceProxy
OrderServiceImpl	OrderServiceProxy
额外功能维护复杂。
额外功能修改复杂

10.6Spring动态代理

1、Spring的动态代理的概念
概念:t通过代理类为原始类增加额外的功能
好处:利于原始(目标类)维护
2、环境搭建
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
    <scope>runtime</scope>
</dependency>

3、Spring 动态代理的开发步骤

1.创建原始对象

public class UserServiceImpl implements UserService {
    public void register(User user) {
        System.out.println("UserServiceImpl.register   业务运算+ dao");
    }

    public void login(String name, String password) {
        System.out.println("UserServiceImpl.login");
    }
}

xml配置

<bean id = "userService" class ="com.chai.service.UserServiceImpl"/>

2.增加额外功能,需要实现MethodBeforAdvice接口

/*
    作用:需要把运行在原始方法执行前运行的额外功能书写在before方法中
*/
public class Before implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("-------------method before advice log---------");
    }
}

xml配置

<bean id ="before" class="com.chai.dynamic.Before"/>

3.定义切入点:

切入点:额外功能加入的位置

目的:有程序员根据自己的需要,决定额外功能加入给那个原始方法
register
login

简单的测试:所有方法都作为切入点,都加入额外功能
<!--    定义切入点-->
<aop:config>
    <aop:pointcut id="pc" expression="execution(* *(..))"/>
</aop:config>

4.组装(整合2.3)

<!--    组装-->
表达的含义是:所有的方法都加入before的额外功能
<aop:advisor advice-ref="before" pointcut-ref="pc"/>

调用:

目的:获得Spring工厂创建的动态代理对象,并进行调用
ApplicationContext context = ClassPathXmlApplicationContext("applicatonContext.xml");

注意:
1、Spring 的工厂通过原始对象的id 值获得的是代理类对象
2、获得代理类对象后,可以通过声明的接口类型对对象存储。(参看静态代理,代理类需要实现与目标类相同的接口)
UserService userService = context.getBean("userservice",UserService.class);
userService.login("","");
userService.register(new User());

//===================================
输出结果
-------------method before advice log---------
UserServiceImpl.register   业务运算+ dao
-------------method before advice log---------
UserServiceImpl.login

在这里插入图片描述

4、动态代理细节分析

1、Spring创建的代理类在哪里?

Spring 框架在运行时,通过动态字节码技术,在JVM中创建的,运行在JVM内部,等程序一结束,会随着JVM一起消失

什么叫做动态字节码技术:通过第三方动态字节码框架,在JVM中创建对应的字节码文件,进而创建对象,当虚拟机结束,动态字节码跟着消失。

结论:动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理类文件加载过多影响项目管理的问题。

在这里插入图片描述

2、动态代理编程简化了代理的开发

在额外功能不变的情况下,创建其他目标类(原始类)的代理对象时,只需要指定原始对象即可
<bean id="orderService" class="com.chai.Service.OrderServiceImpl"/>

3、动态代理额外功能的维护性大大增强

当额外功能更改时,只需要在xml中更改配置即可
<bean id ="before1" class="com.chai.dynamic.Before1"/>

10.7、Spring动态代理详解

1、额外功能的详解

MethodBeforeAdvice分析

MethodBeforeAdvice接口的作用:额外功能运行在原始方法之前,进行额外功能操作。

public class Before implements MethodBeforeAdvice {
    /*
    作用:需要把运行在原始方法执行前运行的额外功能书写在before方法中
    Method :额外功能所增加给的那个原始方法。取决于增加给那个方法。
    Object【】 objects:额外功能所增加给的那个原始方法的参数,
    当增加给的方法是login时  参数就为 String name String password
                register时 参数为User user
    Object o : 额外功能增加给的那个原始对象  UserServiceImpl     OrderServiceImpl
     */
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("-------------method before advice log---------");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ie98C5D6-1599390478106)(Spring.assets/image-20200905104915872.png)]

MethodInterceptor(方法拦截器)

MethodBeforAdvice—> 原始方法执行之前。

MethodInterceptor----->原始方法执行 前、后、 前后都可以

public class Around implements MethodInterceptor {
    /*
    invoke :方法的的作用:额外功能书写在invoke中
            额外功能:原始方法前
                     原始方法后
                     原始方法前、后
   确定:原始方法怎么运行

   参数:
   MethodInvocation :额外功能所增加给的那个原始方法。(类似于MethodBeforeAdvice中的Method参数)
   返回值:Object :原始方法的返回值。

   methodInvocaton.proceed();//原始方法执行。
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("原始方法执行前的额外功能");
        Object result = null;
        try {
            result= methodInvocation.proceed();
        }catch (Exception e){
            System.out.println("原始方法抛出异常时执行的额外功能");
            e.printStackTrace();  
        }
        System.out.println("原始方法执行后的额外功能");
        return result;
    }
}

MethodInterceptor影响原始方法的返回值

原始方法的返回值,直接作为invoke 方法的返回值返回,MethodInterceptor不会影响原始方法的返回值

MethodInterceptor 影响原始方法的返回值
Invoke方法的返回值,不要直接返回原始方法的运行结果就行
public Object invoke (MethodInvocation methodInvocation) thorws Throwable{
		System.outPrintln("原始方法执行前的额外功能");
		Object result = methodInvocation.proceed();
		return false;
}

10.8、切入点详解

切入点决定了额外功能加入的位置

<aop:pointcut id = "pc" expression="exection(* *(..))"/>
exection(* *(..))  ------->匹配了所有的方法,a b c

execution() :切入点函数
* *(..)      切入点表达式
1、切入点表达式

1、方法切入点表达式

在这里插入图片描述

* *(..)
* ------>修饰符,返回值
* ------>方法名
() ----->参数表
..------>对于参数没有要求(参数有几个都行,参数时什么类型都行)

定义login方法作为切入点

* login(..)

# 定义register作为切入点
* register(..)

定义login方法且login方法有两个字符串类型的参数,作为切入点

* login(String,String)
#注意:非java.lang包中的类型,必须要写全限定名
* register(com.chai.pojo.User)

# .. 可以和具体的参数类型连用
* login(String,..) ---> 切入点必须是login方法,且第一个参数类型必须为String,对后边的参数类型不限制,可有可无
可以匹配
login(String,String),  login(String) login(String,com.chai.pojo.User)

上面所讲解的方法的切入点表达式不精准

精准方法切入点限定

修饰符 返回值			包.类.方法(参数)
	*				com.chai.service.UserServiceImpl.login(..)
	*`				com.chai.service.UserServiceImpl.longin(Stirng,String)

2、类切入点:

指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能

语法一:

修饰符 返回值			包.类.方法(参数)
	*				com.chai.service.UserServiceImpl.*(..)

语法二:

#忽略包
1、类只存在一级包 com.UserServiceImpl
     * *.UserServiceImpl.*()
2、 类存在多级包 com.chai.service.UserServiceImpl
	* *..UserServiceImpl.*()

3.包切入点表达式

指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能。

语法:

#切入点包中所有的类,必须在当前包中,不能再当前包的子包中。
 	* com.chai.service.*.*(..)  
#当想要包含子包中的所有类时,包与类之间是..
	* com.chai.service..*.*(..)
2、切入点函数
切入点函数:用于执行切入点函数,功能最全。

1、execution

最为重要的切入点函数,功能最全。
执行: 方法切入点表达式		类切入点表达式		包切入点表达式
弊端:execution 执行切入点表达式,书写麻烦
		execution(* com.chai.service..*.*(..))

#注意:其他的切入点函数只是对execution的简化,功能上是完全一致的。

2、args

作用:主要用于函数(方法)参数的匹配
切入点:方法参数必须是两个字符串类型的参数
	execution(* *(String,String))
	
	args(String,String)

3、within

作用:主要用于进行类、包切入点表达式的匹配
切入点:
	execution(* *..UserServiceImpl.*(..))
	within(*..UserServiceImpl)
	execution(* com.chai.service..*.*(..))
	within(com.chai.service..*)

4、@annotation

作用:为具有特殊注解的方法加入额外功能

<!--        @annotation使用-->
<aop:pointcut id="pc" expression="@annotation(com.chai.annotation.Log)"/>

5、切入点函数的逻辑运算

指的是整合多个切入点函数一起配合工作,进而完成更为复杂的需求

and操作:

案例:login 同时 参数为两个string字符串
	1.execution(* login(String,String))
	2.execution(* login(..)) and args(String,String)
#注意:and 操作不能用于同种类型的切入点函数
案例: register 方法  和 login方法都作为切入点 
	 execution(* register(..)) and execution(* login(..))   这是错误的
	 execution(* register(..)) or  execution(* login(..))

or 操作:

案例: register 方法  和 login方法都作为切入点 
	 execution(* register(..)) or  execution(* login(..))

总结
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值