Spring 下篇
a、为什么要学习代理模式?
因为这就是SpringAOP的底层!
b、代理模式的分类
静态代理
动态代理
c、好处:
可以使真实的角色的操作更加纯粹!不用去关注一些公共的业务
公共业务就交给代理角色!实现了业务的分工!
公共业务发生扩展的时候,方便集中管理
缺点:
一个真实的角色就会发生一个代理角色,代码量会翻倍,开发效率会变低
2、静态代理
角色分析
抽象角色:一般会使用接口或者抽象类来解决
真实角色 :被代理的角色
代理角色:代理真实角色,代理真实的角色后,我们一般会做一些附属操作
客户:访问代理对象的人
一个小案例:客户要租房,房东要出租,
package com.zy.demo01; //租房 public interface Rent { public void rent(); }
2、真实角色
//房东 public class Host implements Rent{ @Override public void rent() { System.out.println("房东要租房子"); } }
3、代理角色
package com.zy.demo01; public class Proxy implements Rent{ private Host host; public Proxy(){ } public Proxy(Host host){ this.host=host; } @Override public void rent() { SeeHost(); Fare(); HeTong(); host.rent(); } //收中介费 public void Fare(){ System.out.println("收中介费"); } //看房 public void SeeHost(){ System.out.println("中介带你去看方法"); } //签合同 public void HeTong(){ System.out.println("签租赁合同"); } }
4、客户端访问代理角色
package com.zy.demo01; public class Client { public static void main(String[] args) { //房东要租房子 Host host = new Host(); /*host.rent();*/ //代理,中间帮房东租房子,但代理会有一些附属操作 Proxy proxy = new Proxy(host); //你不用面对房东,直接去找中介租房 proxy.rent(); } }
深入理解静态代理
public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
//真实的一个对象 public class UserServiceImpl implements UserService{ @Override public void add() { /* System.out.println("使用了add的方法"); * 如果每一个方法都添加,则就会增加它的代码量,程序的基本原则是尽量不要改动源码 * 只是手动添加,所以伪类减少它的代码量,我们弄一个代理*/ System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } }
package com.zy.demo02; //代理角色 public class UserServiceProxy implements UserService{ private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //日志方法 public void log(String msg){ System.out.println("[Debug]打印了"+msg+"方法"); } }
public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); } }
3、动态代理(AOP的底层机制)
动态代理和静态代理的角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理、基于类的动态代理
基于接口:jdk动态代理
基于类:cglib
java字节码实现:javasist
了解两个类:Proxy(代理)、InvocationHandler(调用处理程序)
Proxy:提供了创建动态代理类和实例的静态方法,它也是有这些方法创建的所有动态代理类的超类。
InvocationHandler:是由代理实例的,调用处理程序实现的接口
每一个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用被编码并分派到其调用处理程序的invoke方法中。
入门,
一个小案例
//租房 public interface Rent { public void rent(); }
package com.zy.demo03; //房东 public class Host implements Rent { @Override public void rent() { System.out.println("房东要租房子"); } }
package com.zy.demo03; import sun.reflect.generics.tree.VoidDescriptor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们会用这个类,自动生成代理类! public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 public Rent rent; public void setRent(Rent rent){ this.rent=rent; } //生成代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this); } @Override //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现 seeHost(); Object result = method.invoke(rent, args); fare(); return result; } public void seeHost(){ System.out.println("中介带去看房子"); } public void fare(){ System.out.println("收中介费"); } }
package com.zy.demo03; public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理角色 ProxyInvocationHandler handler = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们调用的接口对象 handler.setRent(host); Rent proxy =(Rent) handler.getProxy(); //这个proxy就是动态生成的,没有手动写 proxy.rent(); } }
加深理解动态代理
package com.zy.demo04; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //我们会用这个类,自动生成代理类! public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 public Object target; public void setTarget(Object target) { this.target = target; } //生成代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现 log(method.getName()); Object result = method.invoke(target, args); return result; } public void log(String msg){ System.out.println("执行了"+msg+"方法"); } }
package com.zy.demo04; import com.zy.demo02.UserService; import com.zy.demo02.UserServiceImpl; public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); ProxyInvocationHandler handler = new ProxyInvocationHandler(); handler.setTarget(userService); //设置要代理的对象 //动态生成代理类 UserService proxy =(UserService) handler.getProxy(); proxy.query(); } }
动态代理的好处:
可以使真实的角色的操作更加纯粹!不用去关注一些公共的业务
公共业务就交给代理角色!实现了业务的分工!
公共业务发生扩展的时候,方便集中管理
一个动态代理类代理的是一个接口,一般就是对应的一类业务
一个动态代理类可以代理多个类,只要是实现了同一个接口即可
4、Aop
什么是AOP?
AOP(Aspect Oriented Programming) 意味:面向切面编程,是通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术,
把系统的共同的业务逻辑形成一个切入点,注入到目标对象中,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。
Aop在Spring中的作用:
提供声明式事务,允许用户自定义切面
横切关注点 | 跨越应用程序多个模块的方法或功能,即使,与我们业务逻辑无关,但我们需要关注部分,就是横向关注点,如:日志、安全、缓存、事务等 |
切面(ASPECT) | 横切关注点,被模块化的特殊对象,即,它是一个类 |
通知 (Advice) | 切面必须要完成的工作,即,它是一个类的一个方法 |
目标 (Target) | 被通知的对象 |
代理(Proxy) | 向目标对象应用通知之后创建的对象 |
切入点(PointCut) | 切面通知执行的”地点“的定义 |
连接点(JointPoint) | 与切入点匹配的执行点 |
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
通知类型 | 连接点 | 实现接口 |
前置通知 | 方法前 |
org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 |
org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 |
org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 |
org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 |
org.springframework.aop.IntroductionInterceptor |
即Aop在不改变原有代码的情况下,去新增新的功能
利用spring实现aop
导入aop的依赖包
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> </dependencies>
实现aop的第一种方式:使用spring的api接口 (主要是spring Api接口实现)
1、接口
public interface UserService { public void add(); public void delete(); public void update(); public void qurey(); }
2、实现类
package com.zy.service; public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void qurey() { System.out.println("查询了一个用户"); } }
3、
package com.zy.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { /* * method:要执行的目标对象的方法 * args:参数 * target:目标对象那个*/ @Override public void before(Method method, Object[] arg, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了"); } }
package com.zy.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue); } }
4、xml文件
<?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--> <bean id="userService" class="com.zy.service.UserServiceImpl"/> <bean id="log" class="com.zy.log.Log"/> <bean id="afterLog" class="com.zy.log.AfterLog"/> <!--方式一:使用原生态spring api接口--> <!--配置aop:需要导入aop约束--> <aop:config> <!--一个切入点 expression:表达式,execution() :需要执行的位置--> <aop:pointcut id="pointcut" expression="execution(* com.zy.service.UserServiceImpl.*())"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
5、测试
import com.zy.service.UserService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理的是一个接口 UserService userService = (UserService) context.getBean("userService"); userService.add(); } }
实现aop的第二种方式:自定义来实现 (主要是切面定义)
package com.zy.diy; //自定义 public class DiyPointCut { public void before(){ System.out.println("+++++++方法执行前+++++++"); } public void after(){ System.out.println("+++++++方法执行后+++++++"); } }
<?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--> <bean id="userService" class="com.zy.service.UserServiceImpl"/> <bean id="log" class="com.zy.log.Log"/> <bean id="afterLog" class="com.zy.log.AfterLog"/> <!--方式二:自定义--> <bean id="diy" class="com.zy.diy.DiyPointCut"/> <aop:config> <!--自定义切面, ref:要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.zy.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>
测试类不变
实现aop的第三种方式:使用注解实现
package com.zy.diy; //使用注解实现aop import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect //标注这个类是一个切面 public class Annot { @Before("execution(* com.zy.service.UserServiceImpl.*(..))") public void before(){ System.out.println("+++++++方法执行前+++++++"); } @After("execution(* com.zy.service.UserServiceImpl.*(..))") public void after(){ System.out.println("+++++++方法执行后+++++++"); } //在环绕增加中,我们可以给定一个参数,道标我们要获取处理切入的点 @Around("execution(* com.zy.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前"); Signature signature = joinPoint.getSignature();//获得签名 System.out.println("signature:" +signature); //执行方法 Object proceed = joinPoint.proceed(); System.out.println("环绕后"); } }
<?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--> <bean id="userService" class="com.zy.service.UserServiceImpl"/> <bean id="log" class="com.zy.log.Log"/> <bean id="afterLog" class="com.zy.log.AfterLog"/> <!--方式三--> <bean id="annot" class="com.zy.diy.Annot"/> <!--开启注解支持 aop动态代理机制jdk(默认 proxy-target-class="false") cglib (proxy-target-class="false")--> <aop:aspectj-autoproxy/> </beans>
测试类不变
5、Spring中的事务管理
1、声明式事务:AOP
2、编程式事务
思考:为什么需要事务?
1、如果不配置事务,可能存在数据提交不一致的情况下,
2、如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务
3、事务在项目开发中十分的重要,涉及到数据的一致性和完整性的问题
小案例
1、实体类
package com.zy.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private int age; }
2、接口,接口的实现类
package com.zy.dao; import com.zy.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); //添加一个用户 int addUser(User user); //删除一个用户 int deleteUser(int id); }
package com.zy.dao; import com.zy.pojo.User; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> selectUser() { User user = new User(55, "小王", 123456); UserMapper mapper = getSqlSession().getMapper(UserMapper.class); mapper.addUser(user); mapper.deleteUser(5); return getSqlSession().getMapper(UserMapper.class).selectUser(); } @Override public int addUser(User user) { int i = getSqlSession().getMapper(UserMapper.class).addUser(user); return i; } @Override public int deleteUser(int id) { int user = getSqlSession().getMapper(UserMapper.class).deleteUser(id); return user; } }
3、接口的xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.zy.dao.UserMapper"> <select id="selectUser" resultType="com.zy.pojo.User"> select * from user; </select> <insert id="addUser" parameterType="com.zy.pojo.User"> insert into user (id,name,age) values (#{id},#{name},#{age}); </insert> <delete id="deleteUser" parameterType="int"> delete from user where id=#{id}; </delete> </mapper>
4、spring-aop.xml
配置声明式事务
<?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" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--datasource:使用spring的数据源替换mybatis 这里使用spring提供的jdbc--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/Mybatis?userUnicode=true;characterEncode=Utf-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!--sqlsessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--绑定mybatis配置文件--> <!-- <property name="configLocation" value="classpath:mapper/mybatis-config.xml"/>--> <property name="mapperLocations" value="classpath:mapper/UserMapper.xml"/> </bean> <!--SqlSessionTemplate :就是我们使用的sqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!--配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--jie'结合aop,实现事务的织入--> <!--配置事务通知:--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给哪些方法配置事务--> <!--配置事务的传播特性:propagation=--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="select" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.zy.dao.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config> </beans>
5、applicationContext.xml
<?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"> <import resource="spring-dao.xml"/> <!----> <bean id="userMapper" class="com.zy.dao.UserMapperImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> </beans>
6、测试
import com.zy.dao.UserMapper; import com.zy.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("mapper/applicationContext.xml"); UserMapper bean = context.getBean("userMapper",UserMapper.class); List<User> users = bean.selectUser(); for (User user : users) { System.out.println(user); } } }