Spring框架学习---Spring Framework下篇

关于上篇学习链接:Spring框架学习—Spring Framework上篇

3.6 静态代理和动态代理

3.6.1 静态代理

  • 生活中的代理(租客通过中介租到房东的房子)(例1)
    在这里插入图片描述

角色分析:
在这里插入图片描述
项目框架:
在这里插入图片描述
Host.Java 相当于现实生活中的房东(准备出租房子的人)
在这里插入图片描述
上面的Rent为接口,表示出租操作
在这里插入图片描述
Proxy.java 相当于现实生活中的中介(房东将房子交给中介进行出租)
在这里插入图片描述

Client.Java相当于现实生活中的租客(准备租房子的人,租客通过中介租到房东的房子)
在这里插入图片描述
运行效果:
在这里插入图片描述
上面提到的四个角色,抽象角色对应Rent接口,真实角色对应房东,代理角色对应中介,客户角色对应租客。

代理模式的优缺点:
在这里插入图片描述

再来一个例子(例2):
项目结构:
在这里插入图片描述

UserService.Java 抽象角色
在这里插入图片描述
UserServiceImpl.Java 真实角色
在这里插入图片描述
Client.Java 客户角色
在这里插入图片描述
运行效果:
在这里插入图片描述

如果要给UserServiceImp里面每一个操作(增删改查)增加一个日志
在这里插入图片描述
像上面这种操作非常的麻烦,我们可以通过上面学到的代理来解决这个问题
首先先编写一个代理类(UserServiceProxy.Java),在代理类中添加日志业务,并在各个方法(增删改查)中实现日志

public class UserServiceProxy implements UserService {
    
    
	private UserService userService;

	public UserServiceProxy(UserService userService) {
    
    
		// TODO Auto-generated constructor stub
		this.userService = userService;
	}

	@Override
	public void add() {
    
    
		// TODO Auto-generated method stub
		log("add");
		userService.add();
	}

	@Override
	public void delete() {
    
    
		// TODO Auto-generated method stub
		log("delete");
		userService.delete();
	}

	@Override
	public void update() {
    
    
		// TODO Auto-generated method stub
		log("update");
		userService.update();
	}

	@Override
	public void query() {
    
    
		// TODO Auto-generated method stub
		log("query");
		userService.query();
	}
	
	//打印日志方法
	public void log(String msg) {
    
    
		System.out.println("[Debug]使用了"+msg+"方法");
	}

}

客户角色通过代理对象实现业务逻辑
在这里插入图片描述
运行效果:
在这里插入图片描述
上面的操作其实是一种横向开发(保持原有方法不变的情况下增加相应业务)
在这里插入图片描述

3.6.2 动态代理

在这里插入图片描述
通过动态代理实现例1:
项目结构:(Host.Java和Rent.Java和实例1无差别)
在这里插入图片描述
ProxyInvocationHandler.Java代理处理程序,用户生成代理,代理的相关操作

public class ProxyInvocationHandler implements InvocationHandler{
    
    
	private 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 {
    
    
		// TODO Auto-generated method stub
		// 中介带租客看房子
		seeHouse();
		// 中介将房子出租给租客
		Object result=method.invoke(rent, args);
		//中介收取中介费
		fare();
		return result;
	}
	
	public void seeHouse() {
    
    
		System.out.println("中介看房子");
	}

	
	public void fare() {
    
    
		System.out.println("中介收费");
	}
}

client.Java 租客通过中介租房子
在这里插入图片描述
运行效果:
在这里插入图片描述

通过动态代理实现例2:
项目框架:
在这里插入图片描述
ProxyInvocationHandler.Java

public class ProxyInvocationHandler implements InvocationHandler{
    
    
	private UserService	 userService;
	
	public void setRent(UserService userService) {
    
    
		this.userService = userService;
	}
	
	//生成得到代理类
	public Object getProxy() {
    
    
		return Proxy.newProxyInstance(this.getClass().getClassLoader(), userService.getClass().getInterfaces()	, this);
	}
	
	// 处理代理实例,并返回结果
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
		// TODO Auto-generated method stub
		//打印日志
		log(method.getName());
		//执行相应业务
		Object result=method.invoke(userService, args);
		return result;
	}
	
	   //打印日志方法
		public void log(String msg) {
    
    
			System.out.println("[Debug]使用了"+msg+"方法");
		}
	
}

Client.Java
在这里插入图片描述
运行效果:
在这里插入图片描述
从上面的实例2我们可以发现其实我们只是稍微修改了一下ProxyInvocationHandler.Java中接口对象,其实我们可以整理出一个模板如下图(将接口对象设置为Object对象):
在这里插入图片描述

具体代码如下:

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);
	}
	
	// 处理代理实例,并返回结果
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
		// TODO Auto-generated method stub
		//打印日志
		log(method.getName());
		//执行相应业务
		Object result=method.invoke(target, args);
		return result;
	}
	
	   //打印日志方法
		public void log(String msg) {
    
    
			System.out.println("[Debug]使用了"+msg+"方法");
		}
	
}


Client.Java中(需要使用的代理的类只需要给setTarget("接口“)传递一个接口对象即可)
在这里插入图片描述
运行效果一致:
在这里插入图片描述

动态代理的好处:
在这里插入图片描述

3.7 AOP

  • 什么是AOP?

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。[1]

简单解释:中文叫做面向切面编程,在不改变源码的情况下给程序动态添加功能的一种技术。
在这里插入图片描述

  • 相关概念
    在这里插入图片描述
  • 实例1

环境搭建
项目结构:
在这里插入图片描述

maven:

<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

在这里插入图片描述
service包:
在这里插入图片描述
在这里插入图片描述
log包:
在这里插入图片描述
在这里插入图片描述
resources包:
先导入aop的头文件

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

配置aop
在这里插入图片描述

测试类:
在这里插入图片描述
运行效果:
在这里插入图片描述

  • 另一种实现方式

新增一个diy包:
在这里插入图片描述
在包中增加一个DiyPointCut.Java类
在这里插入图片描述
修该配置(把原来胡aop配置注释了):
在这里插入图片描述
再次运行测试类:
在这里插入图片描述

  • 通过注解实现
    在DiyPointCut.Java类中,使用注解:
    在这里插入图片描述
    开启注解支持(把前面的aop配置注释掉):
    在这里插入图片描述
    再次运行测试类:
    在这里插入图片描述
  • 环绕切入
    在DiyPointCut.Java类中添加一个around方法
    在这里插入图片描述
    运行测试类(签名就是具体执行的哪一个方法):
    在这里插入图片描述

3.8 Spring整合Mybatis

  • 整合前

项目架构:
在这里插入图片描述

Maven导入相关依赖:
在项目pom.xml中导入以下依赖

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

Mybatis中资源导出失败解决之build配置

 <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

创建dao包:
创建UserMapper接口
在这里插入图片描述
创建UserMapper接口对应的mapper映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserMapper">

    <select id="selectUser" resultType="User">
        select * from user_test
    </select>

</mapper>

创建pojo包:
在包中创建User的实体类
在这里插入图片描述
在resources包中创建mybatis的配置文件(mybatis-config.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>


    <typeAliases>
        <package name="pojo"/>
    </typeAliases>
        <!-- 对事务的管理和连接池的配置 -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8" />
                    <property name="username" value="root" />
                    <property name="password" value="root" />
                </dataSource>
            </environment>
        </environments>


        <mappers>
            <mapper class="dao.UserMapper"></mapper>
        </mappers>
</configuration>

创建测试类MyTest:

public class MyTest {
    
    
    @Test
    public void selectUser() throws IOException {
    
    

        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> userList = mapper.selectUser();
        for (User user: userList){
    
    
            System.out.println(user);
        }

        sqlSession.close();
    }
}

运行selectUser:
在这里插入图片描述

  • 整合后方式一(使用MyBatis-Spring)

什么是MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

相应版本对应:
在这里插入图片描述
导入MyBatis-Spring(在上面的项目中已经导入了该配置)
在这里插入图片描述
配置数据源替换mybaits的数据源:
创建spring-dao.xml
在这里插入图片描述

在spring-dao.xml中添加mybaits的数据源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
    <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/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

在spring-dao.xml中配置SqlSessionFactory,关联MyBatis

    <!--配置SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--关联Mybatis-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:dao/*.xml"/>
    </bean>

在配置文件中((mybatis-config.xml)只留下别名和相关配置即可(配置信息都移到上面去了)
在这里插入图片描述

在spring-dao.xml中注册sqlSessionTemplate,关联sqlSessionFactory;

   <!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--利用构造器注入-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

增加Dao接口的实现类;私有化sqlSessionTemplate
在这里插入图片描述

public class UserDaoImpl implements UserMapper {
    
    

    //sqlSession不用我们自己创建了,Spring来管理
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
    
    
        this.sqlSession = sqlSession;
    }

    public List<User> selectUser() {
    
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }

}

在spring-dao.xml中注册bean实现(初始化sqlSession)

  <bean id="userDao" class="dao.UserDaoImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

测试类中新增测试方法test2

    @Test
    public void test2(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper mapper = (UserMapper) context.getBean("userDao");
        List<User> user = mapper.selectUser();
        System.out.println(user);
    }

运行test2
在这里插入图片描述

  • 整合方式二(通过继承SqlSessionDaoSupport,就无需再配置注册sqlSessionTemplate,因为SqlSessionDaoSupport提供了getSqlSession()可以直接获取SqlSession())

(备注:mybatis-spring1.2.3版才可以使用以下这种方式)

在spring-dao中把sqlSessionTemplate注释掉
在这里插入图片描述
创建一个UserDaorImpl2类,继承SqlSessionDaoSupport并且实现UserMapper接口
在这里插入图片描述
在applicationContext.xml配置中将bean进行修改(初始化sqlSessionFactory)
在这里插入图片描述
再次运行测试2:
在这里插入图片描述

3.9 事物

根据ACID原则一个操作执行要么成功,要么不成功,不存在一部分成功,一部分不成功,为了做到这一点,我们需要使用事务,事务分为编程式事务和声明式事务。

  • 编程式事务
    将事务管理代码嵌到业务方法中来控制事务的提交和回滚,这种方式有一个明显的缺点就是必须在每个事务操作业务逻辑中包含额外的事务管理代码。
  • 声明式事务
    将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,但是这种方式需要Aop的支持,将事务管理作为横切关注点,通过aop方法模块化。

在spring-dao.xml配置文件中:
先导入事务(tx)和aop的头文件
在这里插入图片描述

       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd

配置声明式事务

    <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

结合Aop实现事务的置入,先配置事务通知

    <!--配置事务通知-->
    <tx:advice id="txAdvice" 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="search*" propagation="REQUIRED"/>
            <tx:method name="get" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

7种事务传播,默认REQUIRED
在这里插入图片描述

然后再配置事务的切入点

    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

在UserMapper接口中新增addUser(User user)和delUser(int id)二个方法
在这里插入图片描述
配置这二个方法的映射
在这里插入图片描述
在UserDaorImpl2类中
在这里插入图片描述
在映射文件中,我们故意把delUser的语句设置错误
在这里插入图片描述
测试类中,新增一个test3
在这里插入图片描述
运行情况
在这里插入图片描述
我们查看一下数据库中user_test表的数据,并没有增加(如果不使用事务的话,执行addUser是会成功的,就会增加数据)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42753193/article/details/123144036