目录
4.9 有参构造方法注入(一个是userdao 另一个时 str 配置如下)
测试 转账中间添加了int i=5/0 事务捕捉到异常 数据库未发生改变
4 编写通知类 配置核心文件(两种方法 一种是直接写poingtcut 另一种 是先定义 再用pointcut-ref引入 )
Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架。
提供了表现层 SpringMVC和持久层 Spring JDBC Template以及 业务层 事务管理等众多的企业级应用
技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框
架。
两大核心:以 IOC(Inverse Of Control:控制反转 将创建对象过程交给Spring进行管理)和 AOP(Aspect OrientedProgramming:面向切面编程)为内核。
1)方便解耦,简化开发
Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
什么是耦合度?
对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是耦合,
耦合度过高会使代码的维护成本增加。要尽量解耦
2)AOP编程的支持
Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。
3)声明式事务的支持
通过配置完成事务的管理,无需手动编程
4)方便测试,降低JavaEE API的使用
Spring对Junit4支持,可以使用注解测试
5)方便集成各种优秀框架
不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持

1. 创建java项目,导入自定义IOC相关坐标
2. 编写Dao接口和实现类
3. 编写Service接口和实现类
4. 编写测试代码
依赖
- <dependencies>
- <dependency>
- <groupId>dom4j</groupId>
- <artifactId>dom4j</artifactId>
- <version>1.6.1</version>
- </dependency>
- <dependency>
- <groupId>jaxen</groupId>
- <artifactId>jaxen</artifactId>
- <version>1.1.6</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- </dependency>
- </dependencies>
编写Dao接口和实现类
public interface UserDao { public void save(); }public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存成功"); } }
Service接口和实现类
public interface UserService { public void save(); }public class UserServiceImpl implements UserService { private UserDao userDao; public void save() { userDao = (UserDao) BeanFactory.getBean("userDao"); userDao.save(); } }
beans.xml 文件
<beans>
<!-- bean 表示一个需要创建的对象
id: 表示该对象的名字
class:表示该对象的类型
-->
<bean id="userDao" class="com.qiku.dao.UserDaoImpl"></bean>
</beans>
- package com.qiku.utils;
-
- /**
- * 2022/6/23 15:05
- *
- * @author yrc
- * @version MySpring
- */
-
-
- import org.dom4j.Document;
- import org.dom4j.Element;
- import org.dom4j.io.SAXReader;
- import java.io.InputStream;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- /**
- * 根据配置文件中的信息 创建对象
- * 并将所有的对象存入一个容器之中
- */
- public class BeanFactory {
- //声明一个 map 充当 容器作用 ,用于包存所有的对象
- public static Map<String ,Object> ioc=new HashMap<>();
-
- //程序启动时 自动初始化对象
- static {
-
- try {
- //1 读取配置文件
- InputStream resource =
- BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
- //2 解析xml
- SAXReader saxReader = new SAXReader();
- //2.1 通过saxReader 将resource装换为document
- Document document = saxReader.read(resource);
- //3 编写xPath表达式
- String xPath="//bean";
- //4 获取所有的bean标签
- List<Element> nodes = document.selectNodes(xPath);
- System.out.println(nodes);
- //5 遍历 并创建对象 设置到map中
- for (Element e:nodes){
- String id = e.attributeValue("id");
- String className = e.attributeValue("class");
- System.out.println("id= "+id);
- System.out.println("id= "+className);
- Object obj = Class.forName(className).newInstance();
- //将 id 和obj存入ioc容器
- ioc.put(id,obj);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- //提供获取对象的方法
- public static Object getBean(String beanId){
- return ioc.get(beanId);
- }
-
-
-
-
- }
测试类 以及结果

总结:
* 其实升级后的BeanFactory就是一个简单的Spring的IOC容器所具备的功能。
* 之前我们需要一个userDao实例,需要开发者自己手动创建 new UserDao();
* 现在我们需要一个userdao实例,直接从spring的IOC容器获得,对象的创建权交给了spring控制
* 最终目标:代码解耦合 解耦合并不是没有关系了 只是耦合度降低了

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
1. ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种。
2. FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3. AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
ApplicationContext app =new ClassPathXmlApplicationContext("applicationContext.xml");
<bean id="" class=""></bean>
* 用于配置对象交由Spring来创建。
* 基本属性:
id:Bean实例在Spring容器中的唯一标识
class:Bean的全限定名(全路径)name: 不常用
* 默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功。
<bean id="" class="" scope=""></bean>

<bean id="" class="" scope="" init-method="" destroy-method=""></bean>
* init-method:指定类中的初始化方法名称
* destroy-method:指定类中销毁方法名称
无参构造方法实例化
工厂静态方法实例化
工厂普通方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
<bean id="userDao" class="com.qiku.dao.impl.UserDaoImpl"/>
应用场景
依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
public class StaticFactoryBean {
public static UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="userDao" class="com.qiku.factory.StaticFactoryBean" factory-method="createUserDao" />
应用场景
依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。
public class DynamicFactoryBean {
public UserDao createUserDao(){
return new UserDaoImpl();
}
}
- <bean id="dynamicFactoryBean" class="com.qiku.factory.DynamicFactoryBean"/>
- <bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"/>
依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通过框架把持久层对象传入业务层,而不用我们自己去获取。
- public UserServiceImpl(UserDao userDao,String str) {
- System.out.println("有参构造器执行了 ");
- this.userDao = userDao;
-
- }
- <!-- 配置userDao 将其交给Spring 的容器管理–>-->
- <bean id="userDao" class="com.qiku.dao.impl.UserDaoImpl"></bean>
-
-
- <!-- 配置UserService-->
- <bean id="userService" class="com.qiku.service.impl.UserServiceImpl">
- <!-- (1) 使用有参构造器的 方式注入 index下标0 代表第一个参数
- 1 则代表 第二个参数 type 为参数类型 ref 为引用数据类型 指的是上面 的 id userDao-->
- <!-- <constructor-arg index="0" type="com.qiku.dao.UserDao" ref="userDao"/>-->
- <!-- 第(2)种方法 name表示参数中的名字 代表之前的 index=0-->
- <constructor-arg name="userDao" type="com.qiku.dao.UserDao" ref="userDao"></constructor-arg>
- <constructor-arg name="str" type="java.lang.String" value="123"></constructor-arg>
- </bean>
- public void setUserDao(UserDao userDao) {
- System.out.println("set方法执行了");
- this.userDao = userDao;
- }
配置spring容器利用set方法注入
- <bean id="userDao" class="com.qiku.dao.impl.UserDaoImpl"></bean>
- <bean id="userService2" class="com.qiku.service.impl.UserServiceImpl">
- <property name="userDao" ref="userDao"/>
- </bean>
xmlns:p="http://www.springframework.org/schema/p"
- <!-- p命名空间 注入-->
- <bean id="userDao" class="com.qiku.dao.impl.UserDaoImpl"></bean>
- <bean id="userService3" class="com.qiku.service.impl.UserServiceImpl"
- p:userDao-ref="userDao">
- </bean>
- public class User {
- private String username;
- private String age;
- public void setUsername(String username) {
- this.username = username;
- }
- public void setAge(String age) {
- this.age = age;
- }
- }
- <bean id="user4" class="com.qiku.pojo.User">
-
- <constructor-arg name="name" value="马孟德"></constructor-arg>
- <constructor-arg name="age" value="35"></constructor-arg>
-
- <!-- <property name="name" value="马孟德"></property>-->
- <!-- <property name="age" value="35"></property>-->
- </bean>
User类 还有set 方法
- private List<Object> list;
- private Object[] arrays;
- private Map<String,Integer> map;
- private Properties properties;
配置文件
- <!-- 注入集合类型-->
- <bean id="user5" class="com.qiku.pojo.User">
- <property name="list" >
- <list>
- <value>chaolihai</value>
- <value>bucuoou</value>
- <value>zhadsafds</value>
- <ref bean="userDao"></ref>
- </list>
- </property>
- </bean>
- <!-- 注入数组类型-->
- <bean id="user6" class="com.qiku.pojo.User">
- <property name="arrays" >
- <array>
- <value>chaolihai</value>
- <value>bucuoou</value>
- <value>zhadsafds</value>
- <ref bean="userDao"></ref>
- </array>
- </property>
- </bean>
- <!-- 注入map类型-->
- <bean id="user7" class="com.qiku.pojo.User">
- <property name="map" >
- <map>
- <entry key="嗨嗨嗨" value="8"></entry>
- <entry key="clearlove" value="7"></entry>
- <entry key="老阴逼" value="6"></entry>
- </map>
- </property>
- </bean>
-
- <!-- 注入Properties类型数据-->
- <bean id="user8" class="com.qiku.pojo.User" scope="prototype">
- <property name="properties">
- <props>
- <prop key="driver">com.mysql.jdbc.Driver</prop>
- <prop key="url">jdbc:mysql://localhost:3306/mydb</prop>
- <prop key="username">root</prop>
- <prop key="password">root</prop>
- </props>
- </property>
- </bean>
测试
- //集合注入
- @Test
- public void test10() {
-
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("applicationContext.xml");
- User user = context.getBean("user5", User.class);
- System.out.println(user.getList());
- context.close();
- }
-
- @Test
- public void test11() {
-
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("applicationContext.xml");
- User user = context.getBean("user6", User.class);
- System.out.println(user.getArrays());
- context.close();
- }
- //集合注入
- @Test
- public void test12() {
-
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("applicationContext.xml");
- User user = context.getBean("user7", User.class);
- System.out.println(user.getMap());
- context.close();
- }
- @Test
- public void test13() {
-
- ClassPathXmlApplicationContext context =
- new ClassPathXmlApplicationContext("applicationContext.xml");
- User user = context.getBean("user8", User.class);
- System.out.println(user.getProperties());
- context.close();
- }
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分
配置拆解到其他配置文件中,也就是所谓的配置文件模块化。
1)并列的多个配置文件
ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
2)主从配置文件
<import resource="applicationContext-xxx.xml"/>

<bean>标签:创建对象并放到spring的IOC容器
id属性:在容器中Bean实例的唯一标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean的作用范围,常用是Singleton(默认)和prototype<constructor-arg>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值<property>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值
<list>
<set>
<array>
<map>
<props>
<import>标签:导入其他的Spring的分文件
属性中包含尖括号
属性设置空值
5.1.1 核心配置文件
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- 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
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd"
- >
-
- <!-- spring 加载外部文件-->
- <context:property-placeholder location="classpath:jdbc.properties"/>
- <!-- 将数据库连接池交给IOC容器-->
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="${jdbc.driver}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
-
-
- <!-- 将QueryRunner 交给IOC 容器-->
- <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
- <!--利用有参构造器的方式将 datasource注入 -->
- <constructor-arg name="ds" ref="dataSource"></constructor-arg>
- </bean>
-
- <!-- 将AccountDao 交给IOC-->
- <bean id="accountDao" class="com.qiku.dao.impl.AccountDaoImpl">
- <property name="qr" ref="queryRunner"></property>
- </bean>
-
- <!--把AccountService交给IOC容器-->
- <bean id="accountService" class="com.qiku.service.impl.AccountServiceImpl">
- <property name="accountDao" ref="accountDao"></property>
- </bean>
-
-
-
- </beans>
5.1.2 jdbc配置文件
- jdbc.driver=com.mysql.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=utf8&useSSL=false
- jdbc.username=root
- jdbc.password=root
5.1.3 set方法
queryrunner在Dao层 set注入spring容器



jdbc 配置文件同5.1.2
5.2.1 数据库配置类
- package com.qiku.config;
- /* 数据源配置
- * */
-
- import com.alibaba.druid.pool.DruidDataSource;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.PropertySource;
-
- import javax.sql.DataSource;
-
-
- @PropertySource("classpath:jdbc.properties")
- public class DataSourceConfig {
- //使用EL表达式 读取配置文件中的值 给属性赋值
- @Value("${jdbc.driver}")
- private String driver;
- @Value("${jdbc.url}")
- private String url;
- @Value("${jdbc.username}")
- private String username;
- @Value("${jdbc.password}")
- private String password;
-
-
- @Bean
- public DataSource getDataSource(){
- DruidDataSource dataSource = new DruidDataSource();
- dataSource.setDriverClassName(driver);
- dataSource.setUrl(url);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- return dataSource;
- }
-
-
- }
5.2.2 Spring 核心配置类
- package com.qiku.config;
-
- import org.apache.commons.dbutils.QueryRunner;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Import;
-
- import javax.sql.DataSource;
-
- /**@Configuration 写在类上面
- * 表示该类是一个配置类
- * @ComponentScan 用于开启注解扫描
- *
- * @Import(DataSourceConfig.class)
- * 引入其他配置类
- */
- @Configuration
- @ComponentScan("com.qiku")
- @Import(DataSourceConfig.class)
- public class SpringConfig {
- // @Bean 写在方法上 用于将第三方的类 交给IOC容器
- //默认的beanId同样是雷鸣的首字母小写 体现为方法的返回值类型
- @Bean("queryRunner")
- public QueryRunner getQueryRunner(
- @Autowired DataSource dataSource) {
- return new QueryRunner(dataSource);
- }
-
-
- }
5.2.3 DAO层实现类
- package com.qiku.dao.impl;
-
- import com.qiku.dao.AccountDao;
- import com.qiku.pojo.Account;
- import org.apache.commons.dbutils.QueryRunner;
- import org.apache.commons.dbutils.handlers.BeanHandler;
- import org.apache.commons.dbutils.handlers.BeanListHandler;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Repository;
-
- import java.sql.SQLException;
- import java.util.List;
-
- /**
- * 2022/6/24 15:22
- *
- * @author yrc
- * @version spring02_quickStart
- */
- @Repository
- public class AccountDaoImpl implements AccountDao {
-
- @Autowired
- private QueryRunner qr;
-
- //set 方法 spring注入
- public void setQr(QueryRunner qr) {
- this.qr = qr;
- }
-
- @Override
- public void save(Account account) throws SQLException {
- String sql = "insert into account(name,money) " +
- "values(?,?)";
- int update = qr.update(sql, account.getName(), account.getMoney());
- }
-
- @Override
- public void update(Account account) throws SQLException {
- String sql="update account set name=? ,money=? where id=?";
- int update = qr.update(sql, account.getName(), account.getMoney(), account.getId());
-
- }
-
- @Override
- public void delete(Integer id) throws SQLException {
- String sql="delete from account where id=?";
- qr.update(sql,id);
-
- }
-
- @Override
- public List<Account> findAll() throws SQLException {
- String sql="select * from account";
- List<Account> query = qr.query(sql, new BeanListHandler<Account>(Account.class));
- return query;
- }
-
- @Override
- public Account findById(Integer id) throws SQLException {
- String sql="select * from account where id =?";
- Account query = qr.query(sql, new BeanHandler<Account>(Account.class),id);
- return query;
-
- }
- }
5.2.4 Service层实现类
- package com.qiku.service.impl;
-
- import com.qiku.dao.AccountDao;
- import com.qiku.pojo.Account;
- import com.qiku.service.AccountService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import java.sql.SQLException;
- import java.util.List;
-
- /**
- * 2022/6/24 15:49
- *
- * @author yrc
- * @version spring02_quickStart
- */
- @Service
- public class AccountServiceImpl implements AccountService {
- @Autowired
- private AccountDao accountDao;
-
- public void setAccountDao(AccountDao accountDao) {
- this.accountDao = accountDao;
- }
- @Override
- public void save(Account account) throws SQLException {
- accountDao.save(account);
- }
-
- @Override
- public void update(Account account) throws SQLException {
- accountDao.update(account);
- }
-
- @Override
- public void delete(Integer id) throws SQLException {
- accountDao.delete(id);
- }
-
- @Override
- public List<Account> findAll() throws SQLException {
- List<Account> all = accountDao.findAll();
- return all;
- }
-
- @Override
- public Account findById(Integer id) throws SQLException {
- Account account = accountDao.findById(id);
-
- return account;
- }
-
- }
5.2.5 测试类
- package com.qiku.test;
-
- import com.qiku.config.SpringConfig;
- import com.qiku.pojo.Account;
- import com.qiku.service.AccountService;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.annotation.AnnotationConfigApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import java.sql.SQLException;
- import java.util.List;
-
- /**
- * 2022/6/24 16:37
- *
- * @author yrc
- * @version spring02_quickStart
- */
- public class AccountServiceTest {
- //加载配置类
-
- ApplicationContext context =
- new AnnotationConfigApplicationContext(SpringConfig.class);
-
- // 没有配置文件了
- // private ClassPathXmlApplicationContext context
- // =new ClassPathXmlApplicationContext("applicationContext.xml");
- //从IOC中获取accountService对象
- private AccountService accountService= context.getBean("accountServiceImpl",AccountService.class);
- // 测试报保存信息
- @Test
- public void testSave() throws SQLException {
- Account account=new Account();
- account.setName("lucy");
- account.setMoney(9999.99);
- accountService.save(account);
- }
- @Test
- public void testUpdate() throws SQLException {
- Account account=new Account();
- account.setId(3);
- account.setName("机甲战士");
- account.setMoney(9999.99);
- accountService.update(account);
- }
- @Test
- public void testFindAll() throws SQLException {
- Account account=new Account();
-
- List<Account> all = accountService.findAll();
- System.out.println(all);
- }
- @Test
- public void testFindById() throws SQLException {
- Account account=new Account();
- account.setId(1);
- Account byId = accountService.findById(account.getId());
- System.out.println(byId);
- }
- @Test
- public void testDelete() throws SQLException {
- Account account=new Account();
-
- accountService.delete(4);
-
- }
- }
5.2.6 数据库


- <!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-test</artifactId>
- <version>5.1.5.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.12</version>
- <scope>test</scope>
- </dependency>

- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(classes = {SpringConfig.class})
- public class SpringAndJunitTest {
- @Autowired
- private AccountService accountService;
-
- @Test
- public void testFindALL() throws SQLException {
- List<Account> all = accountService.findAll();
- System.out.println(all);
-
- }
-
- @Autowired
- private DataSource dataSource;
- @Test
- public void test02(){
- System.out.println(dataSource);
- }
- }
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
我们可以将业务代码和事务代码进行拆分,通过动态代理的方式,对业务方法进行事务的增强。这样就不会对业务层产生影响,解决了耦合性的问题啦!
常用的动态代理技术
JDK 代理 : 基于接口的动态代理技术·:利用拦截器(必须实现invocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,从而实现方法增强
CGLIB代理:基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行增强

- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- 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
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd"
- >
- <context:component-scan base-package="com.qiku"></context:component-scan>
- <!-- spring 加载外部文件-->
- <context:property-placeholder location="classpath:jdbc.properties"/>
- <!--<!– 将数据库连接池交给IOC容器–>-->
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="${jdbc.driver}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
-
-
-
- <!-- 将QueryRunner 交给IOC 容器-->
- <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
- <!--利用有参构造器的方式将 datasource注入 -->
- <constructor-arg name="ds" ref="dataSource"></constructor-arg>
- </bean>
-
- </beans>
- package com.qiku.utils;
-
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- import javax.sql.DataSource;
- import java.sql.Connection;
- import java.sql.SQLException;
-
- /**
- * 由于之前的 in out 操作都是独立的食物 并且独立的连接
- * 解决方法:
- * 将获取连接的方式统一管理 以及同一个事务中尽心操作
- *
- * 链接工具类: 从数据源中获取一个链接 并实现线程的绑定
- *
- *
- */
- @Component
- public class ConnectionUtils {
- // 创建本地线程 绑定的是数据库连接
- private ThreadLocal<Connection> threadLocal=new ThreadLocal<>();
-
- // 从IOC容器中 获取数据源对象
- @Autowired
- private DataSource dataSource;
-
- //获取当前线程上的连接
- public Connection getThreadConnection() {
- //1 先从threadlocal里面获取 当前连接
- Connection connection = threadLocal.get();
- //判断connection 是否为null
- if (connection == null) {
-
- try { //则说明是第一次获取 则从数据源中 获取连接 并与线程绑定
- connection = dataSource.getConnection();
- threadLocal.set(connection);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- return connection;
- }
-
- //解除绑定
- public void removeThreadConnection(){
-
- threadLocal.remove();
- }
- }
-
-
-
- package com.qiku.utils;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- import java.sql.SQLException;
-
- /*
- * 事务管理器 :开启事务 提交事务 回滚事务 释放资源
- * */
- @Component
- public class TransactionManager {
-
- @Autowired
- private ConnectionUtils connectionUtils;
-
- //开启事务
- public void beginTransaction(){
- try {
- //开启事务 并关闭自动提交
- connectionUtils.getThreadConnection().setAutoCommit(false);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- public void commit(){
- try {
- //提交事务
- connectionUtils.getThreadConnection().commit();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- public void rollback(){
- try {
- //事务回滚
- connectionUtils.getThreadConnection().rollback();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- public void release(){
- try {
- //改回自动提交
- connectionUtils.getThreadConnection().setAutoCommit(true);
- //归还连接池
- connectionUtils.getThreadConnection().close();
- //解除与线程的绑定
- connectionUtils.removeThreadConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
2.1.1 工厂动态代理类
- package com.qiku.proxy;
-
- import com.qiku.service.AccountService;
- import com.qiku.utils.TransactionManager;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.util.Arrays;
-
- @Component
- public class JdkProxyFactory {
- /*
- * 动态代理类*/
- //要代理的目标对象
- @Autowired
- private AccountService accountService;
- //事务管理器
- @Autowired
- private TransactionManager transactionManager;
-
- //创建AccountService 的代理对象
- public AccountService getAccountServiceProxy(){
- AccountService accountServiceProxy =null;
- accountServiceProxy = (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
- accountService.getClass().getInterfaces(),
- new InvocationHandler() {
- @Override
- // 代理对象 拦截到的的方法transfer() 拦截到的方法的参数
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result=null;
- try{
- // 开启事务
- transactionManager.beginTransaction();
- //执行业务处理
- result= method.invoke(accountService,args);
- //提交事务
- transactionManager.commit();
- }catch (Exception e){
- e.printStackTrace();
- transactionManager.rollback(); //回滚事务
- }finally {
- transactionManager.release();//释放资源
- }
- return result;
- }
- }
- );
- return accountServiceProxy;
- }
-
- }
- package com.qiku.proxy;
-
- import com.qiku.service.AccountService;
- import com.qiku.utils.TransactionManager;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.cglib.proxy.Enhancer;
- import org.springframework.cglib.proxy.MethodInterceptor;
- import org.springframework.cglib.proxy.MethodProxy;
- import org.springframework.stereotype.Component;
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.util.Arrays;
-
- @Component
- public class CGLIBProxyFactory {
-
- @Autowired
- private AccountService accountService;
- @Autowired
- private TransactionManager transactionManager;
-
- public AccountService getAccountServiceProxy(){
- AccountService accountServiceProxy =null;
- /**
- * 参数一 :目标对象的字节码文件
- * 参数二 : 动作类 实现增强功能
- *
- * */
- accountServiceProxy= (AccountService) Enhancer.create(AccountService.class,
- new MethodInterceptor() {
- @Override
- public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- Object result=null;
-
- try {
- transactionManager.beginTransaction();
- result=method.invoke(accountService,objects);
- transactionManager.commit();
- } catch (Exception e) {
- e.printStackTrace();
- transactionManager.rollback();
- } finally {
- transactionManager.release();
- }
- return result;
- }
- });
- return accountServiceProxy;
-
- }
- }

- @Service
- public class AccountServiceImpl implements AccountService {
- @Autowired
- private AccountDao accountDao;
-
- @Override
- public void transfer(String outName, String innerName, Double money) {
- //支出
- accountDao.out(outName,money);
- int i=5/0;
- // 收入
- accountDao.in(innerName,money);
-
-
- }
- }
- @Repository //注册到IOC容器
- public class AccountDaoImpl implements AccountDao {
- //dao层保证同一个链接
- @Autowired
- private QueryRunner queryRunner;
- @Autowired
- private ConnectionUtils connectionUtils;
-
- @Override
- public void out(String outerName, double money) {
-
- try { String sql ="UPDATE account SET money=money-? WHERE name=?";
- queryRunner.update(connectionUtils.getThreadConnection(),sql,money,outerName);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void in(String innerName, double money) {
-
- try { String sql ="UPDATE account SET money=money+? WHERE name=?";
- queryRunner.update(connectionUtils.getThreadConnection(),sql,money,innerName);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- }
3.1 Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:
* Target(目标对象):代理的目标对象
* Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
* Joinpoint(连接点):所谓连接点是指那些可以被拦截到的点,也就是可以被增强的方法。在spring中,这些点指的是方法,因为 spring只支持方法类型的连接点
* Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。真正增强的方法(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )举例 1:对 com.qiku.dao.BookDao 类里面的 add 进行增强execution(*com.qiku.dao.BookDao.add(.. ))举例 2:对 com.qiku.dao.BookDao 类里面的所有的方法进行增强execution( *com.qiku.dao.BookDao.* ( .. ))举例 3:对 com.qiku.dao 包里面所有类,类* Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知 分类:前置通知、后置通知、异常通知、最终通知、环绕通知
当前四个通知组合在一起时,执行顺序如下:
@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)
* Aspect(切面):是切入点和通知(引介)的结合
* Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织 入,而AspectJ采用编译期织入和类装载期织入
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:aop="http://www.springframework.org/schema/aop"
- 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
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!-- 将目标对象 交给 IOC容器-->
- <bean id="accountService" class="com.qiku.service.impl.AccountServiceImpl"></bean>
- <bean id="userService" class="com.qiku.service.impl.UserServiceImpl"></bean>
-
- <!-- 将通知类 交给 IOC容器-->
- <bean id="myAdvice" class="com.qiku.advic.MyAdvice"></bean>
-
- <!-- 配置植入关系 以及切面 方法一-->
- <aop:config>
- <!-- 引入通知类 即切面 myAdvice-->
- <aop:aspect ref="myAdvice">
- <!-- 配置目标类的transfer 方法执行是 使用前置 通知-->
- <!-- pointcut="execution(* com.qiku.service.impl.AccountServiceImpl.*(..))"/>-->
- <aop:before method="before"
-
- pointcut="execution(* com.qiku.service..*.*(..))"/>
- </aop:aspect>
- </aop:config>
- <!-- 方法二-->
- <aop:config>
- <!-- * com.qiku.service..*.*(..) 表示 service下的所有类的所有类型的方法-->
- <aop:pointcut id="myPointcut" expression="execution(* com.qiku.service..*.*(..))"/>
- <aop:aspect ref="myAdvice">
-
- <!-- 前置通知-->
- <aop:before method="before" pointcut-ref="myPointcut"/>
-
- <!-- 后置通知-->
- <aop:after-returning method="afgterReturning" pointcut-ref="myPointcut"/>
-
- <!-- 异常通知 发生异常 后置通知不执行-->
- <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
- <!-- 最终通知 无论是否发生异常 都会执行-->
- <aop:after method="after" pointcut-ref="myPointcut"/>
- <!-- 环绕通知 -->
- <aop:around method="around" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
-
- </beans>
- <!--aop的自动代理-->
- <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
切点表达式的抽取
- @Component
- @Aspect
- public class MyAdvice {
- @Pointcut("execution(* com.qiku..*.*(..))")
- public void myPoint(){}
-
- @Before("MyAdvice.myPoint()")
- public void before() {
- System.out.println("前置通知...");
- }
- }
通知的配置语法:@通知注解(“切点表达式")
名称 标签 说明
前置通知 @Before 用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知 @AfterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行
异常通知 @AfterThrowing 用于配置异常通知。指定增强的方法出现异常后执行
最终通知 @After 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行
环绕通知 @Around 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行注意:
当前四个通知组合在一起时,执行顺序如下:
@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)
- @Configuration
- @ComponentScan("com.qiku") //容器里面注解扫描
- @EnableAspectJAutoProxy //替代 <aop:aspectj-autoproxy />
- public class SpringConfig {
-
- }
依然使用前面的转账案例,将两个代理工厂对象直接删除!改为spring的aop思想来实现
xml配置实现
1)配置文件
- <?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:context="http://www.springframework.org/schema/context"
- 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/context
- http://www.springframework.org/schema/context/spring-context.xsd">
-
- <!--开启组件扫描-->
- <context:component-scan base-package="com.qiku"/>
- <!--加载jdbc配置文件-->
- <context:property-placeholder location="classpath:jdbc.properties"/>
- <!--把数据库连接池交给IOC容器-->
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="${jdbc.driver}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
- <!--把QueryRunner交给IOC容器-->
- <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
- <constructor-arg name="ds" ref="dataSource"></constructor-arg>
- </bean>
- <!--AOP配置-->
- <aop:config>
- <!--切点表达式-->
- <aop:pointcut id="myPointcut" expression="execution(* com.qiku.service..*.*(..))"/>
- <!-- 切面配置 -->
- <aop:aspect ref="transactionManager">
- <aop:before method="beginTransaction" pointcut-ref="myPointcut"/>
- <aop:after-returning method="commit" pointcut-ref="myPointcut"/>
- <aop:after-throwing method="rollback" pointcut-ref="myPointcut"/>
- <aop:after method="release" pointcut-ref="myPointcut"/>
- </aop:aspect>
- </aop:config>
- </beans>
2)事务管理器(通知)
- // 事务管理器工具类,包括:开启事务、提交事务、回滚事务、释放资源
- @Component
- public class TransactionManager {
- @Autowired
- ConnectionUtils connectionUtils;
- public void begin(){
- try {
- connectionUtils.getThreadConnection().setAutoCommit(false);
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public void commit(){
- try {
- connectionUtils.getThreadConnection().commit();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public void rollback(){
- try {
- connectionUtils.getThreadConnection().rollback();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- public void release(){
- try {
- connectionUtils.getThreadConnection().setAutoCommit(true);
- connectionUtils.getThreadConnection().close();
- connectionUtils.removeThreadConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
1)配置文件
- <?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:context="http://www.springframework.org/schema/context"
- 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/context
- http://www.springframework.org/schema/context/spring-context.xsd">
-
- <!--开启组件扫描-->
- <context:component-scan base-package="com.qiku"/>
- <!--开启AOP注解支持-->
- <aop:aspectj-autoproxy/>
- <!--加载jdbc配置文件-->
- <context:property-placeholder location="classpath:jdbc.properties"/>
- <!--把数据库连接池交给IOC容器-->
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
- <property name="driverClassName" value="${jdbc.driver}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
-
- <!--把QueryRunner交给IOC容器-->
- <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
- <constructor-arg name="ds" ref="dataSource"></constructor-arg>
- </bean>
- </beans>
2)事务管理器(通知)
- @Component
- @Aspect
- public class TransactionManager {
- @Autowired
- ConnectionUtils connectionUtils;
-
- @Around("execution(* com.hyq.serivce..*.*(..))")
- public Object around(ProceedingJoinPoint pjp) {
- Object object = null;
- try {
- // 开启事务
- connectionUtils.getThreadConnection().setAutoCommit(false);
- // 业务逻辑
- pjp.proceed();
- // 提交事务
- connectionUtils.getThreadConnection().commit();
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- // 回滚事务
- try {
- connectionUtils.getThreadConnection().rollback();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- } finally {
- try {
- connectionUtils.getThreadConnection().setAutoCommit(true);
- connectionUtils.getThreadConnection().close();
- connectionUtils.removeThreadConnection();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- return object;
- }
- }