一、代理模式(GoF)
1.静态代理
一个原先已有的订单程序,客户想要看到每个订单步骤的耗时,第一种方法可以使用继承,再创建一个类继承原有的目标对象,但由于耦合度高,不采用这种方法。第二种方法采用代理模式(关联关系),耦合度低。创建一个代理类连接公共接口,定义一个接口的属性,若定义目标接口属性则耦合度高,应定义接口的属性,因为此接口还实现了目标对象,类似于多态,再调用方法。
代理模式:将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系耦合度低,代理对象含有目标对象的引用。这里要写一个公共接口类型,因为公共接口耦合度低。
公共接口:
package com.hei;
public interface OrderService {//公共接口
//生成订单
void generate();
//修改订单
void modif();
//查看订单详情
void detail();
}
目标对象:
package com.hei;
public class OrderImp implements OrderService{//目标对象
@Override
public void generate() {
try {
Thread.sleep(2455);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("订单正在生成...");
}
@Override
public void modif() {
try {
Thread.sleep(1655);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("订单正在修改...");
}
@Override
public void detail() {
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("正在查看订单详情...");
}
}
代理对象:
package com.hei;
public class OrderGof implements OrderService{//代理对象,增强功能
private OrderService target;//传目标对象
public OrderGof(OrderService target) {
this.target = target;
}
@Override
public void generate() {//代理方法
long start=System.currentTimeMillis();
target.generate();
long end=System.currentTimeMillis();
System.out.println("耗时时间:"+(end-start));
}
@Override
public void modif() {
long start=System.currentTimeMillis();
target.modif();
long end=System.currentTimeMillis();
System.out.println("耗时时间:"+(end-start));
}
@Override
public void detail() {
long start=System.currentTimeMillis();
target.detail();
long end=System.currentTimeMillis();
System.out.println("耗时时间:"+(end-start));
}
}
主类:
public class Main {
public static void main(String[] args) {
//创建目标对象
OrderService target=new OrderImp();
//创建代理对象
OrderService Gof=new OrderGof(target);
//调用代理方法
Gof.generate();
Gof.modif();
Gof.detail();
}
}
2.动态代理
1)JDK动态代理
JDK动态代理只能代理接口。
JDK动态代理,在内存在自动生成代理类,需要在主类中创建代理对象即可,创建代理对象需注意三个参数:类加载器、接口、调用处理器。
创建代理对象:newProxyInstance.
三个参数(类加载器、接口、调用处理器)
类加载器:
接口:
调用处理器:
InvocatonHandler接口的方法的三个参数:
invoke方法执行,使用method来调用目标对象的目标接口。
具体代码
公共接口:
package com.hei;
public interface OrderService {//公共接口
String getName();
//生成订单
void generate();
//修改订单
void modif();
//查看订单详情
void detail();
}
目标对象类:
package com.hei;
public class OrderImp implements OrderService{//目标对象
@Override
public String getName() {
System.out.println("获得名字");
return "张三";
}
@Override
public void generate() {
try {
Thread.sleep(2455);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("订单正在生成...");
}
@Override
public void modif() {
try {
Thread.sleep(1655);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("订单正在修改...");
}
@Override
public void detail() {
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("正在查看订单详情...");
}
}
InvocatonHandler接口类:
package com.hei;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
private Object target;//目标对象
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start=System.currentTimeMillis();
//调用目标对象的目标方法
Object result= method.invoke(target,args);
long end=System.currentTimeMillis();
System.out.println("耗时"+(end-start)+"毫秒");
return result;
}
}
主类:
package com.hei;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
//创建目标对象
OrderService target=new OrderImp();
//创建代理对象
OrderService OS=(OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),new TimeInvocationHandler(target));
//调用代理对象的方法
OS.generate();
OS.modif();
OS.detail();
String name=OS.getName();
System.out.println(name);
}
}
2)CGLIB动态代理
了解即可
二、面向切面编程(AOP)
把与业务逻辑没有关系的代码,提取出来叫做面向切面编程。
1.七大术语
连接点、切点、通知、切面。
2.切点表达式
3.Spring AOP基于注解
目标类:
package com.hei.service;
import org.springframework.stereotype.Service;
//纳入Spring容器进行管理,需要添加注解
@Service
public class UserService {//目标类
public void login(){//目标方法
System.out.println("系统正在进行身份验证");
}
public void logout(){
System.out.println("退出系统");
}
}
切面:
package com.hei.service;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
//切面=切点+通知
// 通知就是增强,就是具体要编写的增强代码
//通知有五类通知,@通知(切点表达式)
@Before("execution(* com.hei.service.UserService.*(..))")
public void Strong(){
System.out.println("这是一段增强代码,前置通知");
}
}
spring.xml:
开启自动代理:Spring容器在扫描类的时候,查看该类是否有@Aspect注解,若有,则给这个类生成代理对象。
proxy-target-class="true"是强制使用CGLIB动态代理。
proxy-target-class="false"这是默认值,表示接口使用JDK动态代理。
<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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.hei.service"></context:component-scan>
<!-- 开启aspectj-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
测试类:
public class Test {
@org.junit.Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
UserService u= applicationContext.getBean("userService", UserService.class);
u.login();
u.logout();
}
}
通知类型
环绕通知是最大的范围。
切面:
@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
//切面=切点+通知
// 通知就是增强,就是具体要编写的增强代码
//通知有五类通知,@通知(切点表达式)
@Before("execution(* com.hei.service.UserService.*(..))")
public void Strong(){
System.out.println("这是一段增强代码,前置通知");
}
@Around("execution(* com.hei.service.UserService.*(..))")
public void rong(ProceedingJoinPoint p) throws Throwable {
System.out.println("前环绕");
//执行目标
p.proceed();
System.out.println("后环绕");
}
}
切面顺序
利用@Order(数字),数字越小,越先执行。
通用切点
创建一个方法即可,方法里不需要写任何代码,只是作为一个标记。
@Component
@Aspect //切面是使用这个标注的
public class LogAspect{//切面
//通用切点
@Pointcut("execution(* com.hei.service.UserService.*(..))")
public void Normal(){
}
//切面=切点+通知
// 通知就是增强,就是具体要编写的增强代码
//通知有五类通知,@通知(切点表达式)
@Before("Normal()")
public void Strong(){
System.out.println("这是一段增强代码,前置通知");
}
@Around("Normal()")
public void rong(ProceedingJoinPoint p) throws Throwable {
System.out.println("前环绕");
//执行目标
p.proceed();
System.out.println("后环绕");
}
}
连接点
4.Spring AOP基于XML
目标类:
package com.hei.service;
public class UserService {//目标类
public void login(){//目标方法
System.out.println("系统正在登录");
}
}
切面:
package com.hei.service;
import org.aspectj.lang.ProceedingJoinPoint;
public class TimeAspect {
public void round(ProceedingJoinPoint p) throws Throwable {//环绕
long start=System.currentTimeMillis();
p.proceed();//执行目标
long end=System.currentTimeMillis();
System.out.println("耗时"+(end-start)+"毫秒");
}
}
spring.xml(配置文件):
<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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="com.hei.service.UserService"></bean>
<bean id="timeAspect" class="com.hei.service.TimeAspect"></bean>
<!-- aop配置-->
<aop:config>
<!-- 切点-->
<aop:pointcut id="point" expression="execution(* com.hei.service.UserService.*(..))"/>
<!-- 切面-->
<aop:aspect ref="timeAspect">
<aop:around method="round" pointcut-ref="point"></aop:around>
</aop:aspect>
</aop:config>
</beans>
测试类:
public class Test {
@org.junit.Test
public void test(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
UserService u= applicationContext.getBean("userservice", UserService.class);
u.login();
}
}