代理模式(aop实现)

代理模式(proxy):是一种设计模式,不改变原代码的情况下增加功能
静态代理:在编写代码的阶段就以及确定要执行代码、指向对象等等
动态代理:在程序运行阶段根据实际的情况执行不同的代码、指向不同对象等等,jdk自带的和cglib实现

静态代理演示

interface PlayBasketball{
	public void play();
}
class Ikun implements PlayBasketball{
	@Override
	public void play() {
		System.out.println("ikun最棒");
	}
}
class IkunAgent implements PlayBasketball{
	private PlayBasketball target;
	@Override
	public void play() {
		System.out.println("ikun说要先谈钱");
		target.play();
		System.out.println("好的,打完收工,拿钱");
	}
	public IkunAgent(PlayBasketball target) {
		super();
		this.target = target;
	}
}
public class Demo {
	public static void main(String[] args) {
		IkunAgent proxy = new IkunAgent(new Ikun());
		proxy.play();
		/*控制台的输出结果如下:
		ikun说要先谈钱
		ikun最棒
		好的,打完收工,拿钱
		*/
	}
}

由代码可以看出,这静态代理和web开发中service、dao层的逻辑关系完全一样。

动态代理中的JDK代理:是javaJDK自带的一种代理方式

interface BookDao{
	public void addBook();
}
class BookDaoImpl implements BookDao{
	@Override
	public void addBook() {
		System.out.println("执行SQL");
	}
}
class ProxyFactory{
	private Object target;
	public ProxyFactory(Object target) {//这样写可以代理很多东西
		super();
		this.target = target;
	}
	public Object getProxyInstance() {
		return Proxy.newProxyInstance(
				ProxyFactory.class.getClassLoader(),    //取类加载器,任何类都可以去取,因为类加载器只有一个
				target.getClass().getInterfaces(), 		//获得接口
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("开启事务");
						Object result = method.invoke(target, args);
						System.out.println("提交事务");
						return result;
					}
				});
	}
}
public class Demo2 {
	public static void main(String[] args) {
		ProxyFactory factory = new ProxyFactory(new BookDaoImpl());  //动态代理,这里参数带谁就代理谁
		Object object = factory.getProxyInstance();     //调用factory的方法
		if(object instanceof BookDao) {
			BookDao bookDao=(BookDao)object;
			bookDao.addBook();
		}
	}
}

动态代理之cglib代理,需要导包

class StudentDao{
	public void addStu() {
		System.out.println("执行SQL操作数据库");
	}
}
//创建工厂类:用来生成代理对象的,实现MethodInterceptor接口:拦截方法的执行,
//只要调用指定的方法就会被拦截
class CglibProxyFactory implements MethodInterceptor{
	//维护被代理对象
	private Object target;

	public CglibProxyFactory(Object target) {
		super();
		this.target = target;
	}
	//生成代理对象
	public Object getProxyInstance() {
		//1.创建工具类对象:生成代理对象
		Enhancer enhancer = new Enhancer();
		//2.设置父类:设置代理类的父类
		enhancer.setSuperclass(target.getClass());
		//3.设置回调函数
		enhancer.setCallback(this);//拦截方法之后到哪里去找intercept()方法
		//4.创建子类对象
		Object object = enhancer.create();
		return object;
	}
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("开启事务....");
		//执行被代理对象原有的功能
	 	Object result = method.invoke(target, args);
	 	System.out.println("提交事务....");
		return result;
	}
}
public class Demo3 {
	public static void main(String[] args) {
		CglibProxyFactory factory = new CglibProxyFactory(new StudentDao());
		Object object = factory.getProxyInstance();
		if (object instanceof StudentDao) {
			System.out.println("yes");
			StudentDao studentDao = (StudentDao)object;
			studentDao.addStu();
		}
	}
}

特别注意
被代理类在有实现接口的时候一般用jdk代理,而在被代理类没有实现任何接口时可以使用cglib代理,cglib是通过导包实现的,并且cglib生成的代理类是被代理类的子类

重要扩展
AOP面向切面由正是由动态代理实现的,如果被代理对象有实现接口,默认JDK代理生成代理类,在获取代理对象时应该用接口接收对象,如果代理类没有实现接口,会采用cglib代理的方法,生成代理类、代理对象,应该用父类去接收代理对象。即如下:

BankService bankService = (BankService)ac.getBean("bankService");//有接口
Bank bank = (Bank)ac.getBean("bank");                            //无接口
<!--引入约束文件-->
xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 2.配置切面:先导入aop的命名空间、约束文件 -->

<aop:config>
	<!-- 配置切点:通过切入点表达式找到对应的方法 -->
	<aop:pointcut expression="execution(* com.woniuxy.test.BankServiceImpl.sendMony())" id="pc"/>
	<!-- 给切点方法添加通知 -->
	<aop:advisor advice-ref="beforeAdvice" pointcut-ref="pc"/>
	<aop:advisor advice-ref="afterAdvice" pointcut-ref="pc"/>
</aop:config>
<bean id="beforeAdvice" class="com.woniuxy.test.BeforeAdvice"></bean>
<bean id="afterAdvice" class="com.woniuxy.test.MyAfterAdvice"></bean>

声明式事务:上面这种切法是schema-base方式,每一个通知都要编写对应的类,其实不好用
更多的是使用aspectj方式,只需要编写一个切面类,就可以实现很多个方法,作为不同的通知,如下:

public class Advice {
	public void before() {
		System.out.println("开启事务........");
	}
	public void after() {
		System.out.println("提交事务........");
	}
	public void aferReturn() {
		System.out.println("返回后通知");
	}
	public void around() {
		System.out.println("环绕通知");
	}
	public void exception() {
		System.out.println("异常通知:回滚");
	}	
}

xml的配置方式和schema差不多

<!-- aspectj方式 -->
<!-- 1.配置切面类对象 -->
<bean id="advices" class="com.woniuxy.advice.Advice"></bean>

<!-- 2.配置切面 -->
<aop:config>
	<!-- 配置切点:给哪些方法添加通知 -->
	<aop:pointcut expression="execution(* com.woniuxy.test.BankServiceImpl.sendMoney())" 
		id="pc"/>
	<!-- 给切点配置通知 
		ref:引用通知类对象,说明通知在该类里面
	-->
	<aop:aspect ref="advices">
		<!-- 前置通知 -->
		<aop:before method="before" pointcut-ref="pc"/>
		<!-- 后置 -->
		<aop:after method="after" pointcut-ref="pc"/>
	</aop:aspect>
</aop:config>



<!-- 4.service -->
<bean id="bankService" class="com.woniuxy.test.BankServiceImpl"></bean>

所以我们一般采用声明式事务,当然他也是可以用注解的。

@Component
@Aspect  //将当前类设置为切面类
public class Advice {
	@Pointcut("execution(* com.woniuxy.test.BankServiceImpl.sendMoney())")
	public void pointCut() {	
	}
	@Before("pointCut()")
	public void before() {
		System.out.println("开启事务........");
	}
	@After("pointCut()")
	public void after() {
		System.out.println("提交事务........");
	}
	@AfterReturning("pointCut()")
	public void aferReturn() {
		System.out.println("返回后通知");
	}
	@Around("pointCut()")
	public void around() {
		System.out.println("环绕通知");
	}
	@AfterThrowing("pointCut()")
	public void exception() {
		System.out.println("异常通知:回滚");
	}	
}

aop的扫描(切面编程)和id的扫描(IOC)

<!-- 开启aop的注解扫描 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 配置扫描 -->
<context:component-scan base-package="com.woniuxy.*"></context:component-scan>

猜你喜欢

转载自blog.csdn.net/qq_41750712/article/details/90671996