1、代理模式
代理模式分为静态代理和动态代理。
1.1、静态代理
静态代理的思想类似于架构 在两个物体之间添加一个中间件 让这个中间件区将两个物体连接
-
抽象角色 : 一般使用接口或者抽象类来实现
-
真实角色 : 被代理的角色
-
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
-
客户 : 使用代理角色来进行一些操作
举个例子 就像是租房一样
//租房这件事
public interface rent {
void rent();
}
//真实角色:房东要出租房子
public class Host implements rent{
public void rent(){
System.out.println("房东出租房子");
}
}
//代理:中介
public class Proxy {
private Host host;
public Proxy(Host host) {
this.host = host;
}
public void rent(){
host.rent();
look();
hetong();
shouzou();
}
public void shouzou(){
System.out.println("中介收租");
}
public void look(){
System.out.println("看房");
}
public void hetong(){
System.out.println("签合同");
}
}
//客户找房子
package com.llf.Pojo;
public class Client {
public static void main(String[] args) {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
静态代理的好处:
- 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
- 公共的业务由代理来完成 . 实现了业务的分工 ,
- 公共业务发生扩展时变得更加集中和方便 .
缺点 :
- 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
聊聊AOP:纵向开发,横向开发
1.2、动态代理
动态代理的实现有两种 一种是基于接口实现的 一种是基于类的动态代理
- 基于接口的动态代理----JDK动态代理
- 基于类的动态代理–cglib
在静态代理中 我们的代理类是写死的 而在我们的动态代理中 代理类是有我们自动生成的
在这之前 我们需要了解动态代理的两个类 Proxy和InvocationHandler
Proxy:用来获取代理类 通过将需要代理的对象放入 返回一个代理方法 getProxy()
InvocationHandler :
Object invoke(Object proxy, 方法 method, Object[] args);
参数
proxy - 调用该方法的代理实例
method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
生成代理类
/**
* this.getClass().getClassLoader() 得到类路径
* target.getClass().getInterfaces() 得到target的接口
* @return 得到代理类 重要的是第二个参数 放入被代理对象的接口
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
代码实现
package com.ProxyUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
/**
* this.getClass().getClassLoader() 得到类路径
* target.getClass().getInterfaces() 得到target的接口
* @return 得到代理类
*/
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//返回代理类的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object invoke = method.invoke(target, args);
return invoke;
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
测试
package com.ProxyUtil;
import com.ll.Service;
import com.ll.ServiceImpl;
public class Client {
//创建真实对象
public static void main(String[] args) {
ServiceImpl service = new ServiceImpl();
//得到代理类
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(service);
Service proxy = (Service) proxyInvocationHandler.getProxy();
proxy.delete();
}
}
2、AOP(面向切面编程)
思想:在不影响原来业务类的情况下 实现动态增强
2.1、导入织入接口依赖
<!--AOP织入-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
2.2、编写业务类接口和实现类
public interface Service {
void add();
void delete();
void update();
void select();
}
public class ServiceImlp implements Service {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void select() {
System.out.println("查询了一个用户");
}
}
2.3、编写Log类 用来表示需要添加的日志消息
// 第一种方法
//运行前输出
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法");
}
}
//运行后输出
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("返回值为"+returnValue);
}
}
//第二种方法的类
public class log {
public void before(){
System.out.println("==================运行前=====================");
}
public void after(){
System.out.println("==================运行后==========================");
}
}
2.4、编写配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="serviceImpl" class="com.llf.Pojo.ServiceImlp"></bean>
<bean id="afterLog" class="com.Log.AfterLog"></bean>
<bean id="before" class="com.Log.BeforeLog"></bean>
<!--第一种方式 使用Spring API实现AOP 它是一个类-->
<!--切面织入
expression 表达式
execution 参数值:第一个 修饰符 可以是*
第二个 哪个包
第三个 哪个类
第四个 哪个方法 用*表示该类的所有方法
第五个 什么参数 用..表示参数
-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.llf.Pojo.ServiceImlp.*(..))"/>
<!--执行环绕
advice-ref:执行哪一个bean进行环绕
pointcut-ref:为哪一个切入点执行环绕 即织入点的id
-->
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
<!--第二种方式 使用自定义类实现AOP 它是一个方法-->
<!--注册bean-->
<bean id="log" class="com.Log.log"></bean>
<!--定义切面
aspect 切面的名字 即注册的日志信息的Bean ID
pointcut 切入点的位置
method 需要执行的方法
before/after 设置在程序运行前 或者运行后加入日志
-->
<aop:config>
<aop:aspect ref="log">
<aop:pointcut id="point" expression="execution(* com.llf.Pojo.ServiceImlp.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
package com.Log;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect//放在类的上边 用来定义一个切面
public class AnnocationPointCut {
@Before("execution(* com.llf.Pojo.ServiceImlp.*(..))")
public void before(){
System.out.println("运行前");
}
@After("execution(* com.llf.Pojo.ServiceImlp.*(..))")
public void after(){
System.out.println("运行后");
}
}
2.5、测试
import com.llf.Pojo.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void ServiceTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//被代理的对象必须是一个接口
//通过接口类反射得到Service这个接口对象
Service service = context.getBean("serviceImpl", Service.class);
service.add();
}
}