9、代理模式
9.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作(增加功能)
- 客户:访问代理对象的人
代理模式的好处
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务也就交给了代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,开发效率就变低了
9.2、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是直接写好的
- 动态代理分为两大类:1.基于接口的动态代理 2.基于类的动态代理
- 基于接口——JDK的动态代理【使用这个】
- 基于类:cglib
- java字节码实现:javalist
需要了解两个类,Proxy,InvocationHandler
动态代理的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务也就交给了代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要实现了同一个接口即可
代理类:
public class ProxyinvocationHandler implements InvocationHandler {
//需要代理的目标
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//动态生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Logger();
method.invoke(target,args);
shouZu();
return null;
}
private void Logger(){
System.out.println(new Date() + "出租房子");
}
private void shouZu(){
System.out.println("最总房租为***元");
}
}
测试:
@Test
public void test(){
ProxyinvocationHandler handler = new ProxyinvocationHandler();
//注入被代理的对象
handler.setTarget(new Host());
//返回代理对象
Rent proxy = (Rent) handler.getProxy();
//代理对象调用方法
proxy.rent();
}
10、AOP
10.1、AOP的概述
AOP就是切面编程,通过编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
10.2、AOP在spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关,但需要关注的部分就是横切关注点。如:日志、安全、缓存、事务…
- 切面(ASPECT):横切关注点被模块化的特殊对象(它是一个类)
- 通知(Advice):切面必须要完成的工作(它是类中的方法)
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象应用通知后创建的对象
- 切入点(PointCut):切面通知执行的位置
- 连接点(JointPoint):与切入点匹配的执行点
10.3、使用spring实现AOP
使用AOP织入,需要导入依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
1、方式一:使用spring的API接口 【主要springAPI接口实现】
-
日志类
前置增强
public class Log implements MethodBeforeAdvice { /** * @param method 方法 * @param args 参数 * @param target 目标对象 * @throws Throwable */ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了"); } }
后置增强
public class AfterLog implements AfterReturningAdvice { //returnValue返回值 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" + method.getName() + "方法,返回结果:" +returnValue); } }
-
xml配置
<bean id="userServiceImpl" class="com.bin.service.UserServiceImpl"/> <bean id="afterLog" class="com.bin.log.AfterLog"/> <bean id="log" class="com.bin.log.Log"/> <!--配置aop:需要导入aop约束--> <aop:config> <!--切入点:expression;表达式execution(要执行的位置: 访问权限(可省略) 返回值类型 全类名(可省略).方法(参数) 所抛出的异常(可省略))--> <aop:pointcut id="pointcut" expression="execution(* *.*(..))"/> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config>
-
测试类
@Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userServiceImpl = (UserService) context.getBean("userServiceImpl"); userServiceImpl.add(); System.out.println(); userServiceImpl.delete(); System.out.println(); userServiceImpl.update(); }
2、方式二:自定义来实现AOP 【主要是切面定义】
<bean id="diy" class="com.bin.diy.DiyPointCut"/>
<aop:config>
<!--自定义切面,ref要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.bin.service.UserServiceImpl.*(..))"/>
<!--
通知:将方法引用到标签中
根据要求可以设置成:前置通知/后置通知...
-->
<aop:after method="hou" pointcut-ref="pointcut"/>
<aop:before method="qian" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
3、方式三:使用注解实现
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.bin.service.*.*(..))")
public void before(){
System.out.println("执行前");
}
@After("execution(* com.bin.service.*.*(..))")
public void after(){
System.out.println("执行后");
}
//在环绕增强中,可以给定一个参数,代表获取处理切入的点
@Around("execution(* com.bin.service.UserServiceImpl.*(..))")
public void surround(ProceedingJoinPoint jp) throws Throwable {
System.out.println("---------环绕前---------");
Object proceed = jp.proceed();
System.out.println(proceed + "\n" + jp.getSignature());
System.out.println("---------环绕后---------");
}
}
-
@Aspect
- 表示该类为切面
-
@Before(“execution()”)
- 前置增强
-
@After(“execution()”)
- 后置增强
-
@Around(“execution()”)
- 环绕增强
-
@AfterReturning
-
@AfterThrowing
-
execution()表达式内写:修饰符 返回值 包.类.方法(…) 异常
- 修饰符、包、类、异常可以省略不写
- … 表示任意个
- *表示任意的
11、整合Mybatis
-
导入依赖:
- junit
- mybatis
- mysql-connector-java【mysql驱动】
- spring-webmvc【spring】
- aspectjweaver【aop织入】
- mybatis-spring
- spring-jdbc【spring管理jdbc所需依赖】
- druid【阿里数据库连接池】
- spring-tx【spring事务】
<dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--aop依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--mysql驱动依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--mybatis整合依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!--spring事务--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.9</version> </dependency> <!--数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.6</version> </dependency> <!--spring管理jdbc的依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.9</version> </dependency> </dependencies>
11.1、方式一:由spring得到每个接口的代理对象
xml配置:
<!--读取配置文件location:指定属性配置文件的路径-->
<context:property-placeholder location="classpath:db.properties"/>
<!--声明数据源-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${db.url} }"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="myDataSource"/>
<!--指定mybatis主配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--创建Dao接口的实现类对象,MapperScannerConfigurer内部调用getMapper()生成每个接口的代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象,能获取SqlSession-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定Dao接口的包名,每个接口都会执行一次getMapper()得到Dao对象-->
<property name="basePackage" value="com.bin.dao"/>
</bean>
<bean id="userService" class="com.bin.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
11.2、方式二:使用SqlSessionTemplate
xml配置:
<!--读取配置文件location:指定属性配置文件的路径-->
<context:property-placeholder location="classpath:db.properties"/>
<!--声明数据源-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${db.url} }"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="myDataSource"/>
<!--指定mybatis主配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--SqlSessionTemplate线程安全的类,可以直接获得sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.bin.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
<bean id="userService" class="com.bin.service.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
实现类:
public class UserMapperImpl implements UserMapper {
SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
return sqlSession.getMapper(UserMapper.class).selectUser();
}
}
11.3、方式三:继承SqlSessionDaoSupport类
- 实际上和方法二差不多,只是在DAO实现类中少写了一个SqlSessionTemplate属性
xml配置:
<!--读取配置文件location:指定属性配置文件的路径-->
<context:property-placeholder location="classpath:db.properties"/>
<!--声明数据源-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${db.url} }"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="myDataSource"/>
<!--指定mybatis主配置文件-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<bean id="userMapper02" class="com.bin.dao.UserMapperImpl02">
<!--
也可以使用SqlSessionTemplate
但是SqlSessionDaoSupport会自动创建SqlSessionTemplate
最终使用的还是由SqlSessionTemplate得到的sqlSession
-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userService" class="com.bin.service.UserServiceImpl">
<property name="userMapper" ref="userMapper02"/>
</bean>
实现类:
public class UserMapperImpl02 extends SqlSessionDaoSupport implements UserMapper {
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
public User selectUserById(int id) {
return getSqlSession().getMapper(UserMapper.class).selectUserById(id);
}
}
12、事务
-
事务ACID原则
- 原子性
- 一致性
- 隔离性
- 持久性
-
spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中进行事务管理(try,catch语句实现)
-
xml配置
<!--配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean> <!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给哪些方法添加事务--> <tx:attributes> <tx:method name="add*"/> <tx:method name="select*" read-only="true"/> <tx:method name="delete*"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.bin.service.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
-
Spring中七种Propagation类的事务属性详解:
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。