推荐相关文章 Spring 与 Spring IOC
一、代理模式
了解Spring AOP,需要先了解代理模式,因为Spring AOP的底层就是代理模式。代理模式可以比喻为现实中的房地产中介。
代理模式的分类
- 动态代理
- 静态代理
1.1 静态代理
角色分析
- 抽象角色:一般使用接口或抽象类来解决
- 真实角色:别代理的角色
- 代理角色:代理真实角色,代理真实角色后,可以做一些附属操作
- 客户:访问代理对象的人
静态代理模式的好处 - 可使真实角色的操作更加存粹,不用关注一些公共的业务
- 公共业务交给代理角色,实现业务分工
- 公共业务发生拓展时,方便集中管理
静态代理模式的缺点
- 一个真实角色就会产生一个代理,代码量会增加
代码片段
接口
public interface Rent {
void rent();
}
真实角色
public class Host {
public void rent(){
System.out.println("房东要出租房子");
}
}
代理
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
seeHouse();
sign();
fare();
}
public void seeHouse(){
System.out.println("中介带你看房");
}
public void fare(){
System.out.println("收中介费");
}
public void sign(){
System.out.println("通过中介签订租赁合同");
}
}
客户访问代理
public class Client {
@Test
public void clientRentFromHost() {
Host host = new Host();
host.rent();//直接找房东
}
@Test
public void clientRentFromProxy() {
Host host = new Host();
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
1.2 动态代理
- 动态代理角色和静态代理角色一样
- 动态代理类是动态生成的,原理就是反射
- 动态代理分为两类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理(这是很多东西的底层)
- 基于类:cglib
- Java字节码实现:JAVAsist
了解动态代理需掌握两个类:Proxy 与 InvocationHandler
代码片段:
接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
真实角色
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 query() {
System.out.println("查询了一个用户的信息");
}
}
动态代理
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
//获得代理对象
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public void log(String msg){
System.out.println("[debug] 执行了"+ msg +"方法");
}
}
客户端访问代理
public class Client {
@Test
public void test() {
//真实角色
UserServiceImpl userService = new UserServiceImpl();
//代理角色:现在没有,需要动态生成
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用处理程序处理角色来处理我们要调用的接口对象
pih.setTarget(userService);
//proxy 动态生成
UserService proxy = (UserService) pih.getProxy();
proxy.add();
}
}
动态代理的好处
- 可使真实角色的操作更加存粹,不用关注一些公共的业务
- 公共业务交给代理角色,实现业务分工
- 公共业务发生拓展时,方便集中管理
- 一个动态代理类代理的是一个接口,一般对应一类业务
- 一个动态代理类可以代理多个实现了同一接口的类
动态代理执行过程
二、Spring 中的AOP
2.1 AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2.2 AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能.既是,与我们业务逻辑无关,但是我们需要关注的部分,就是横切关注点.如日志,安全,缓存,事务等…
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 前置
- 后置
- 环绕
- 异常
- 引介
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
2.3 Spring AOP案例
接口
public interface UserService {
void add();
void delete();
void update();
void query();
}
实现
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 query() {
System.out.println("查询了一个用户的信息");
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.st.service.UserServiceImpl"/>
</beans>
2.3.1 通过Spring API实现
<bean id="beforeLog" class="com.st.log.BeforeLog"/>
<bean id="afterLog" class="com.st.log.AfterLog"/>
<!--方式一:Spring API-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointCut" expression="execution(* com.st.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointCut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointCut"/>
</aop:config>
public class BeforeLog implements MethodBeforeAdvice {
//method 要执行的方法 args 方法参数 target 目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("[Debug] "+ target.getClass().getName() + "的" + method.getName() + "被执行");
}
}
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("[Debug] 执行了"+ method.getName() + "方法,返回结果为:" + returnValue);
}
}
2.3.2 通过自定义切入点实现
public class DiyPointCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
<!--方式二:自定义-->
<bean id="diyPointCut" class="com.st.diy.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diyPointCut">
<aop:pointcut id="point" expression="execution(* com.st.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
2.3.3 通过注解
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.st.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
@After("execution(* com.st.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("after");
}
@Around("execution(* com.st.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before");
System.out.println("signature " + joinPoint.getSignature());
Object proceed = joinPoint.proceed();
System.out.println("around after");
}
}
<!--方式三:注解-->
<bean id="annotationPointCut" class="com.st.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
三、Spring 与 Mybatis
3.1 mybatis
参考 Mybatis
3.2 Spring 整合 mybatis
mybatis-spring官方文档
大致步骤:
1、配置jdbc驱动以及数据库连接信息
2、配置 sqlSessionFactory
3、配置 sqlSessionTemplate(其实就是 sqlSession)
4、配置接口的实现的bean
以上步骤与mybatis基本一致(获得sqlSessionFactory,获得sqlSession),只是多了配置mapper的实现类。为什么会多一个实现类,为了能够通过IOC注入。
3.2.1 配置
spring.xml 注意:<constructor-arg index="0" ref="sqlSessionFactory"/> ,这里是通过有参构造函数的方式IOC注入,看了SqlSessionTemplate的定义就知道了,其没有set方法,但有一个有参的构造函数
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:com/st/mapper/**/*Mapper.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
applicationContext.xml 注意这段配置 <property name="mapperLocations" value="classpath*:com/st/mapper/*Mapper.xml"/> ,使用的是 “classpath*:”,带有 * 号,而不是classpath:
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="spring.xml"/>
<bean id="userMapper" class="com.st.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<typeAlias type="com.st.pojo.User" alias="User"/>
</typeAliases>
</configuration>
3.2.2 接口、实现及测试
User
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
UserMapper
public interface UserMapper {
List<User> getUserList();
User getUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
List<User> getUserByLimit(Map<String, Object> map);
}
UserMapperImpl 注意: UserMapperImpl 的无参构造以及setSqlSessionTemplate方法是必须的
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
public UserMapperImpl() {
}
public UserMapperImpl(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<User> getUserList() {
return sqlSessionTemplate.getMapper(UserMapper.class).getUserList();
}
@Override
public User getUserById(int id) {
return null;
}
@Override
public int addUser(User user) {
return 0;
}
@Override
public int updateUser(User user) {
return 0;
}
@Override
public int deleteUser(int id) {
return 0;
}
@Override
public List<User> getUserByLimit(Map<String, Object> map) {
return null;
}
}
测试
public class MyTest {
@Test
public void test(){
//与mybatis以前查询方式不同,不再需要通过编写代码获取sqlSession
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = context.getBean(UserMapper.class);
for (User user : mapper.getUserList()) {
System.out.println(user);
}
}
}
还有SqlSessionDaoSupport的实现方式。
三、Spring 声明式事务
3.1 事务
3.1.2 声明式事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="interceptor" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED" />
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" propagation="REQUIRED"/>
<!--<tx:method name="*"/> read-only="true"-->
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="tx_pointCut" expression="execution(* com.st.mapper.*.*(..))"/>
<aop:advisor advice-ref="interceptor" pointcut-ref="tx_pointCut"/>
</aop:config>
propagation的配置方式
- REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
3.1.3 编程式事务
编程式事务参考官方文档