代理模式
代理模式的概念
代理模式(Proxy Pattern):代理模式是 Java 常见的设计模式之一。所谓代理模式是指客 户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。通俗的 来讲代理模式就是我们生活中常见的中介
代理模式的好处
- 隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类 对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
- 开闭原则:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额 外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合 代码设计的开闭原则
- 优点:
- 代理模式能将代理对象与真实对象被调用的目标对象分离。
- 一定程度上降低了系统的耦合度,扩展性好
- 保护目标对象。
- 增强目标对象
- 缺点:
- 代理模式会造成系统设计中类的数目的增加
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
- 增加了系统的复杂度
静态代理
静态代理:代理类由程序员创建的然后编译成.class文件。但是其中缺点是,具有重复代码,灵活性不好,例如在执行某个接口中所有方法之前加上日志逻辑,那么使用静态代理的话,在代理类中每个方法都得加。
组成:
- 一个公共的接口
- 一个被代理角色
- 一个代理角色
实现
- 创建公共的接口
public interface Rent { void renting(); }
- 创建被代理的对象
public class Lanh implements Rent {
@Override
public void renting() {
System.out.println("Lanh有房出租");
}
}
- 创建代理对象
public class StaticProxyRent implements Rent {
private Rent rent;
public StaticProxyRent(Rent rent){
this.rent = rent;
}
@Override
public void renting() {
System.out.println("向房客出租房屋");
this.rent.renting();
System.out.println("完成售后服务");
}
}
- 实现代理
public class StaticProxyTest {
public static void main(String[] args) {
Rent rent = new Lanh();
StaticProxyRent staticProxyRent = new StaticProxyRent(rent);
staticProxyRent.renting();
}
}
动态代理
动态代理:是在运行的时候,通过jvm中的反射进行动态创建对象,生成字节码对象,传入由我们实现该接口的对象,通过反射创建代理对象。 然后当调用代理对象的任何方法都会调用接口中的 invoke(Object proxy,Method method,Object[] args)传入当前代理对象、当前调用的方法、方法参数值
JDK动态代理
使用 JDK 的 Proxy 类实现动态代理
注意:JDK 的动态代理机制只能代理实现了接口的类,而对于没有实现接口的类就不能使用 JDK 的 Proxy 类生成代理对象
- 创建业务接口
public interface JdkProxyRent { void renting(); }
- 创建接口实现类
public class JdkProxyLanh implements JdkProxyRent {
@Override
public void renting() {
System.out.println("Lanh有房出租");
}
}
- 创建切面对象
public class MyAspect {
public void before(){
System.out.println("带领房客看房。。。签租房协议");
}
public void after(){
System.out.println("售后服务。");
}
}
- 创建生成代理对象的工厂
public class JdkProxyFactory {
public static Object getProxyBean(Object target){
Class cls = target.getClass();
MyAspect myAspect = new MyAspect();
//在 JDK 中动态生成代理对象的方法
return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), new InvocationHandler() {
/** 动态生成代理对象中的方法。
* @param proxy 动态生成的代理对象
* @param method 目标方法的方法对象
* @param args 传递到目标方法中的参数列表
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.before();
Object obj = method.invoke(target,args);
myAspect.after();
return obj;
}
});
}
- 实现代理
public class JdkProxyTest {
public static void main(String[] args) {
JdkProxyRent jdkProxyRent = new JdkProxyLanh();
JdkProxyRent proxyRent = (JdkProxyRent) JdkProxyFactory.getProxyBean(jdkProxyRent);
Rent.renting();
}
}
CGLIB动态代理
使用 CGLIB 实现动态代理
注意:cglib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类并通过回调的方式来实现增强,但因为采用的是继承,所以不能对 final 修饰的 类进行代理
- 创建业务接口
public interface CglibProxyRent { void renting(); }
- 创建接口实现类
public class CglibProxyLanh implements CglibProxyRent {
@Override
public void renting() {
System.out.println("Lanh有房出租");
}
}
- 创建切面对象
public class CglibMyAspect {
public void before(){
System.out.println("带领客户看房,签订租房协议");
}
public void after(){
System.out.println("售后服务");
}
}
- 创建生成代理对象的工厂
public class CglibProxyBeanFactory {
public static Object getProxyBean(CglibProxyRent rent){
CglibMyAspect myAspect = new CglibMyAspect();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(rent.getClass());
enhancer.setCallback(new MethodInterceptor() {
/**
* @param o 代理对象的引用
* @param method 目标对象的方法对象
* @param objects 目标方法的参数列表
* @param methodProxy 目标方法的方法对象的代理对象
* @throws Throwable */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
myAspect.before();
Object obj = method.invoke(rent,objects);
myAspect.after();
return obj;
}
});
return enhancer.create();
}
}
- 实现代理
public class Test {
public static void main(String[] args) {
CglibProxyRent rent = new CglibProxyLanh();
CglibProxyRent proxyRent = (CglibProxyRent)CglibProxyBeanFactory.getProxyBean(rent);
proxyRent.renting();
}
}
注意:静态代理跟动态代理,在功能上最明显的表现是,静态代理每代理一个新方法,就要写一遍代理(即使是这些代码在其他方法增强时已经写过),动态代理则是同样的代理逻辑的方法均可以使用同一段代码进行代理
AOP
AOP 的全称是 Aspect Oriented Programming,即面向切面编程,它将业务逻辑的各个部 分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。
- 作用:在不修改代码的情况下实现对程序的拓展
- 实现方式
- SpringAOP模块:切面需要实现通知接口
- AspectJ:切面不需要实现通知接口
- Schema-Based:切面需要实现通知接口
Spring中使用AOP模块
使用 org.springframework.aop.framework.ProxyFactoryBean 工厂对象创建代理对象
- 创建接口
public interface UsersService { void addUsers(); }
- 创建被代理对象
public class UsersServiceImpl implements UsersService {
@Override
public void addUsers() {
System.out.println("addUsers.....");
}
}
- 创建切面
public class ToUppercaseAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//将 username 转换大写
Object[] args = methodInvocation.getArguments();
args[0] = ((String)args[0]).toUpperCase();
Object obj = methodInvocation.proceed();
return obj;
}
}
- 修改Spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置目标对象-->
<bean id="usersService" class="com.lanh.service.impl.UsersServiceImpl"/>
<!--配置切面对象-->
<bean id="toUppercaseAspect" class="com.lanh.aop.ToUppercaseAspect"/>
<!--配置切面-->
<bean id="usersServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置目标对象所实现的接口-->
<property name="proxyInterfaces" value="com.lanh.service.UsersService"/>
<!-- 配置目标对象-->
<property name="target" ref="usersService"/>
<!--配置切面对象-->
<property name="interceptorNames">
<list>
<value>toUppercaseAspect</value>
</list>
</property>
<!--如何生成代理对象 true:使用 CGLIB,false 使用 JDK 的 Proxy-->
<property name="proxyTargetClass" value="true"/>
</bean>
</beans>
- 实现代理
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UsersService usersService = (UsersService)applicationContext.getBean("usersServiceProxy");
usersService.addUsers();
}
}
AspectJ实现切面
- 创建接口
public interface UsersService { void addUsers(String username); }
- 创建代理对象
public class UsersServiceImpl implements UsersService {
@Override
public void addUsers(String username) {
String str = null;
str.toUpperCase();
System.out.println("AddUsers "+username);
}
}
- 创建切面
public class MyAspect {
public void myAspectBefore(JoinPoint joinPoint){
System.out.println("MyAspect Before");
}
public void myAspectAfter(JoinPoint joinPoint){
System.out.println("MyAspect After");
}
}
- 开启aop命名空间
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标对象-->
<bean id="usersService" class="com.lanh.aspectj.service.impl.UsersServiceImpl"/>
<!--配置切面对象-->
<bean id="myAspect" class="com.lanh.aspectj.aop.MyAspect"/>
<!-- 配置切面-->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 配置切点-->
<aop:pointcut id="myPointcut" expression="execution(* com.lanh.aspectj.service.*.*(..))"/>
<!--前置通知-->
<aop:before method="myBefore" pointcut-ref="myPointcut"/>
<!--后置通知-->
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
<!--环绕通知-->
<aop:around method="myAround" pointcut-ref="myPointcut"/>
<!--配置异常通知-->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/>
<!--最终通知-->
<aop:after method="myAfter" pointcut-ref="myPointcut"/> </aop:aspect>
</aop:config>
</beans>
Schema-Based
不写了不写了,23:25了,还没吃晚饭、洗澡。这个就留着吧,这叫什么?这叫留白!是经典的写作手法(手动doge)