Spring入门【一】
一、spring的概述
1. spring 的 开源的轻量级框架
2. spring的两大核心:IOC ,AOP
Spring框架是一个开源的Java平台,它为企业级应用开发提供了全面的解决方案。自从2003年首次发布以来,Spring框架已经成为了Java开发者的首选之一。Spring框架的核心目标是简化企业级应用的开发和维护,通过提供一致、灵活且易于扩展的编程模型,帮助开发者更快地构建高质量的应用程序。
在过去的几年里,Spring框架不断发展壮大,引入了许多新的功能和技术。例如,Spring Boot项目使得开发者能够快速搭建和运行基于Spring的应用程序,无需繁琐的配置和依赖管理。此外,Spring Cloud项目为微服务架构提供了一套完整的解决方案,包括服务发现、配置中心、断路器等功能。
Spring框架的核心组件包括:
- Spring Core:提供了一组基础功能,如依赖注入(DI)、切面编程(AOP)和事务管理等。
- Spring AOP:提供了面向切面编程的支持,允许开发者将横切关注点(如日志记录、安全性检查等)与业务逻辑分离。
- Spring DAO:提供了对JDBC和ORM框架的支持,简化了数据访问层的开发。
- Spring MVC:提供了一种基于请求-响应模式的Web应用程序框架,支持RESTful风格的API设计。
- Spring WebFlux:基于Reactor和Project Reactor构建的异步Web框架,支持非阻塞式IO操作和响应式编程模型。
- Spring Data:提供了一组用于访问各种数据存储技术的抽象层,如JPA、MongoDB、Redis等。
- Spring Security:提供了一套安全框架,用于保护Spring应用程序免受未经授权访问和其他安全威胁。
总之,Spring框架以其强大的功能、灵活的设计和广泛的社区支持,成为了Java开发者在企业级应用开发中不可或缺的一部分。无论是构建简单的Web应用程序还是复杂的企业级系统,Spring框架都能帮助开发者轻松实现高效、可扩展和可靠的应用程序。
二、spring的EJB的区别
1. EJB可以说像是一个Web Service,但也不完全是,比如EJB将编写好的业务组件放置在EJB容器上,然后提供接口给客户端访问;但是功能不仅限如此,EJB标准中提供了很多规范等,而这些规范只有在EJB容器才能正常运行。
EJB重量级框架
2. Spring容器取代了原有的EJB容器,因此以Spring框架为核心的应用无须EJB容器支持,可以在Web容器中运行。
Spring容器管理的不再是复杂的EJB组件,而是POJO(Plain Old Java Object) Bean。
Spring轻量级框架
三、耦合和解耦
1. 什么是耦合
模块之间的关联程度, 依赖程度
2. 什么是解耦
降低模块之间的耦合度(依赖关系)
3. 解耦的目的
编译器不依赖,运行期才依赖
4. 解耦思路
1) 把全限类名都放到配置文件中
2) 通过工厂帮助创建对象
四、解耦代码–自定义IOC
try {
Driver driver = new Driver();
//注册驱动
DriverManager.registerDriver(driver);
} catch (SQLException e) {
e.printStackTrace();
}
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
五、控制反转-- 自定义IOC
1. 引入依赖
<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>
2. 配置文件
<beans>
<!--
用bean标签配置所有的bean对象
id: 对象的唯一的标志
class:类名
-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
3. 创建BeanFactory工厂对象
package com.itheima.factory;
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;
/**
* 创建bean对象的工厂类
*/
public class BeanFactory {
private static Map<String ,Object> map = new HashMap<>();
//提前把所有的对象创建出来,存储
//Map :因为有查找需求 --相当于容器对象-- 包含了所有的对象
//静态代码块
static{
//获取配置文件的输入流对象
InputStream inputStream = BeanFactory.class.getResourceAsStream("/beans.xml");
//解析xml获取xml中所有的信息
SAXReader reader = new SAXReader();
try {
//创建文档对象
Document doc = reader.read(inputStream);
//获取根节点
Element root = doc.getRootElement();
//获取根节点中所有的子节点
//element("bean") : 获取第一个叫bean的子节点
//elements("bean") : 获取所有叫bean的子节点
//elements() : 获取所有的子节点
List<Element> beanList = root.elements("bean");
//获取每一个bean的id和class
for (Element element : beanList) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//通过className全限类名创建对象--获取字节码
Class clazz = Class.forName(className);
//通过反射创建对象
Object obj = clazz.newInstance();
//存储在Map集合中
//key: id
//value:obj
map.put(id ,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//需要实现的功能,传入一个名字,获取一个Bean对象
public static Object getBean(String name){
return map.get(name);
}
}
4. 测试
Object userService1 = BeanFactory.getBean("userService");
System.out.println(userService1);
Object userService2 = BeanFactory.getBean("userService");
System.out.println(userService2);
六、spring的IOC入门(掌握)
1. 引入依赖
<!--引入spring 的最核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2. 创建beans.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--
http://www.springframework.org/schema/beans: 引入bean的名称空间
约束:现在xml的书写
dtd:mybatis
schema:spring
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd 引入约束
-->
<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">
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
</beans>
3. 创建容器对象,根据id获取对象
//创建spring的IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
//获取对象
Object userDao = ac.getBean("userDao");
System.out.println(userDao);
六、IOC的细节
1、容器对象的类结构图
a、beanFactory 是spring容器的顶层接口
b、接口ApplicationContext是 beanFactory子接口
实现类:ClassPathXmlApplicationContext -- 从类路径之下读取配置文件 (常用)
FileSystemXmlApplicationContext - 从绝对路径指定的配置文件读取
AnnotationConfigApplicationContext -- 纯注解配置使用的类 (常用)
c、BeanFactory与ApplicationContext区别
Resource resource = new ClassPathResource("beans.xml");
// BeanFactory:创建容器对象时,只是加载了配置文件,没有创建对象
// 获取对象时:创建对象
BeanFactory beanFactory = new XmlBeanFactory(resource);
//
Object userDao = beanFactory.getBean("userDao");
System.out.println(userDao);
//创建spring的IOC容器
//在创建容器对象时,创建对象 , (常用)
//ApplicationContext:在创建容器时只创建单例模式的对象
// 多例模式的对象,在获取时创建
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Object userDao2 = ac.getBean("userDao");
System.out.println(userDao2);
2、getBean方法
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//根据名称获取该对象
// Object userDao = ac.getBean("userDao");
// System.out.println(userDao);
// 根据类型获取该对象
// 如果该类型有两个实现类,会执行异常
// UserDao userDao = ac.getBean(UserDao.class);
// System.out.println(userDao);
// 得到是id为userDao1的对象,类型为UserDao接口的实现类
UserDao userDao = ac.getBean("userDao2", UserDao.class);
System.out.println(userDao);
3、bean对象的范围和生命周期
springIOC容器默认的单例模式: 单例模式的对象在创建容器时创建,销毁容器时销毁
scope:prototype 原型模式(多例模式):获取时创建,当对象长时间不在被引用, 则被垃圾回收机制回收
scope:singleton :单例模式
scope :request: 请求范围
scope: session : 回话范围
scope:global session :全局范围 -- spring 5的新特性中被删除了
UserDao userDao1 = ac.getBean("userDao", UserDao.class);
System.out.println(userDao1);
UserDao userDao2 = ac.getBean("userDao", UserDao.class);
System.out.println(userDao2);
4、实例化bean的三种方法
1 .<bean id="name" class="全限类名">
getBean(name)
2. 根据静态工厂获取对象
/**
* 通过静态工厂获取对象
*/
public class StaticFactory {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
<!--通过静态工厂创建UserDao对象-->
<!--
factory-method: 工厂方法,返回UserDao对象的方法名
-->
<bean id="userDao" class="com.itheima.factory.StaticFactory" factory-method="getUserDao"></bean>
3. 根据实例(非静态)工厂获取对象
/**
* 实例工厂创建对象
*/
public class InstanceFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
<!--创建实例工厂对象-->
<bean id="instanceFatory" class="com.itheima.factory.InstanceFactory"></bean>
<!--通过实例工厂创建UserDao对象-->
<bean id="userDao2" factory-bean="instanceFatory" factory-method="getUserDao"></bean>
5、依赖注入
a.什么是依赖注入
业务层需要持久层的对象,在配置文件中给业务层传入持久层的对象,就是依赖注入
b. ioc
控制反转包含了依赖注入和依赖查找
6、构造方法注入
<!--依赖注入-->
value 属性只能赋值简单的类型:基本数据类型和String类型
ref: pojo类型,复杂类型, 关联创建好的对象
<!--默认创建对象方式,使用默认的空的构造方法创建对象-->
<bean id="user" class="com.itheima.domain.User">
<!--通过构造方法参数的索引赋值-->
<!--<constructor-arg index="0" value="1"></constructor-arg>-->
<!--<constructor-arg index="1" value="张三"></constructor-arg>-->
<!--通过构造方法参数类型赋值-->
<!--<constructor-arg type="java.lang.Integer" value="2"></constructor-arg>-->
<!--<constructor-arg type="java.lang.String" value="李四"></constructor-arg>-->
<!--通过构造方法参数名字赋值-->
<constructor-arg name="id" value="3"></constructor-arg>
<constructor-arg name="username" value="王五"></constructor-arg>
<constructor-arg name="sex" value="男"></constructor-arg>
<constructor-arg name="birthday" ref="birthday"></constructor-arg>
</bean>
<bean id="birthday" class="java.util.Date"></bean>
7、set方法注入属性(常用)
<!--通过set方法注入-->
<bean id="user2" class="com.itheima.domain.User">
<!--property :属性注入 , 先找到set方法,才能最终找到属性-->
<property name="username" value="王朝"></property>
<property name="birthday" ref="birthday"></property>
<property name="sex" value="男"></property>
<property name="id" value="4"></property>
</bean>
8、p名称空间注入:基于set方法注入
a、在头部文件中引入p名称空间
b、使用p名称空间注入属性
<bean id="user3" class="com.itheima.domain.User"
p:id="5" p:username="马汉" p:sex="男" p:birthday-ref="birthday"
></bean>
9、注入集合属性
<!--注入集合属性-->
<bean id="user4" class="com.itheima.domain.User">
<!--array ,list ,set 结构相同,标签可以混用-->
<property name="list">
<set>
<value>javaSE</value>
<value>javaEE</value>
<value>javaME</value>
</set>
</property>
<property name="set">
<array>
<value>javaSE</value>
<value>javaEE</value>
<value>javaME</value>
</array>
</property>
<property name="strs">
<list>
<value>javaSE</value>
<value>javaEE</value>
<value>javaME</value>
</list>
</property>
<!--map集合properties结构相同,可以通用-->
<property name="map">
<props>
<prop key="五" >five</prop>
<prop key="六" >six</prop>
<prop key="七" >seven</prop>
</props>
</property>
<property name="properties">
<map>
<!--键值对配置-->
<entry key="一" value="one"></entry>
<entry key="二" value="two"></entry>
<entry key="三" value="three"></entry>
<entry key="四" value="four"></entry>
</map>
</property>
</bean>
Spring入门【二】
一、使用IOC完成改造CRUD
a、引入依赖
<!--spring的核心包(基本)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--dbutils-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<!--c3p0数据源-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
b、创建表
create table account(
id int primary key auto_increment,
name varchar(40),
money float
) character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
c、实体类
public class Account {
private Integer id;
private String name;
private Float money;
}
d、持久层
1 接口
package com.torlesse.dao;
import com.torlesse.domain.Account;
import java.util.List;
public interface AccountDao {
/**
* 查询全部
* @return
*/
public List<Account> findAll();
/**
* 根据id查询
* @param id
* @return
*/
public Account findById(Integer id);
/**
* 保存账户
* @param account
*/
public void save(Account account);
/**
* 更新账户
* @param account
*/
public void update(Account account);
/**
* 根据id删除账户
* @param id
*/
public void del(Integer id);
}
2. 实现类
package com.torlesse.dao.impl;
import com.torlesse.dao.AccountDao;
import com.torlesse.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
/**
* dbUtils 操作数据库
* 入口:QueryRunner对象
*/
public class AccountDaoImpl implements AccountDao {
QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public List<Account> findAll() {
String sql = "select * from account";
try {
List<Account> accountList = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));
return accountList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public Account findById(Integer id) {
String sql = "select * from account where id = ?";
try {
Account account = queryRunner.query(sql, new BeanHandler<>(Account.class), id);
return account;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void save(Account account) {
String sql = "insert into account values(null , ? , ?)";
try {
queryRunner.update(sql ,account.getName() ,account.getMoney());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void update(Account account) {
String sql = "update account set name = ? ,money = ? where id = ?";
try {
queryRunner.update(sql ,account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void del(Integer id) {
String sql = "delete from account where id = ?";
try {
queryRunner.update(sql ,id);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
e、业务层
1. 接口
package com.torlesse.service;
import com.torlesse.domain.Account;
import java.util.List;
public interface AccountService {
/**
* 查询全部
* @return
*/
public List<Account> findAll();
/**
* 根据id查询
* @param id
* @return
*/
public Account findById(Integer id);
/**
* 保存账户
* @param account
*/
public void save(Account account);
/**
* 更新账户
* @param account
*/
public void update(Account account);
/**
* 根据id删除账户
* @param id
*/
public void del(Integer id);
}
2. 实现类
package com.torlesse.service.impl;
import com.torlesse.dao.AccountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import java.util.List;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public Account findById(Integer id) {
return accountDao.findById(id);
}
@Override
public void save(Account account) {
accountDao.save(account);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public void del(Integer id) {
accountDao.del(id);
}
}
f、配置文件
<?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">
<!--在测试时需要用到Service对象,创建service对象-->
<bean id="accountService" class="com.torlesse.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao" ></property>
</bean>
<!--在AccountService中需要AccountDao对象-->
<bean id="accountDao" class="com.torlesse.dao.impl.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"></property>
</bean>
<!--在AccountDao中需要QueryRunner对象,创建对象-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!--按照类型在构造方法注入数据源对象-->
<constructor-arg type="javax.sql.DataSource" ref="dataSource"></constructor-arg>
</bean>
<!--在QueryRunner对象中需要DataSource对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!--通过set方法注入必要的四个属性-->
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_330"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
g、测试类
package com.torlesse;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import com.torlesse.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class TestCRUD {
@Test
public void testFindAll(){
//创建容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//创建service对象
AccountService accountService = ac.getBean("accountService",AccountService.class);
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
@Test
public void testFindById(){
//创建service对象
AccountService accountService = new AccountServiceImpl();
Account account = accountService.findById(1);
System.out.println(account);
}
@Test
public void testSave(){
//创建service对象
AccountService accountService = new AccountServiceImpl();
Account account = new Account();
account.setName("ddd");
account.setMoney(1111F);
accountService.save(account);
}
@Test
public void testUpdate(){
//创建service对象
AccountService accountService = new AccountServiceImpl();
Account account = new Account();
account.setId(4);
account.setName("eeee");
account.setMoney(1222F);
accountService.update(account);
}
@Test
public void testDel(){
AccountService accountService = new AccountServiceImpl();
accountService.del(4);
}
}
二、常用的注解
a. 注解开发入门流程
1.引入依赖
<!--引入spring的核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--引入单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2. 配置文件:applicationContext.xml
<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
引入context名称空间-引入约束
base-package:指定要扫描的包, 扫描的是包及其子包
-->
<context:component-scan base-package="com.torlesse"></context:component-scan>
<!--
context:include-filter :指定包含过滤
type="annotation": 按照类型过滤
expression: 过滤的表达式
只扫描标记了Controller注解的类
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
<!--
context:exclude-filter:指定排除过滤
type="annotation": 按照类型过滤
expression:过滤的表达式
排除标记了Controller的注解都会扫描
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</beans>
3. 在需要创建对象的类上添加注解
@Component
4. 测试
//创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取对象
UserDao userDao = ac.getBean(UserDao.class);
System.out.println(userDao);
UserService userService = ac.getBean(UserService.class);
System.out.println(userService);
b. @Component -- 标记在类上,不能用在方法上
作用:创建对象, 只要标记了,扫描了该包,对象就会创建
衍生了三个子注解
@Controller 一般用于web(控制层)层
@Service 一般用于业务层
@Repository 一般用于持久层
相当于xml
<bean id="" class="全限类名"></bean>
属性:value="userDao" 相当于xml的 id="userDao"
如果没有指定value属性,默认的名称是 简单类名,首字母小写
UserDaoImpl -- userDaoImpl
UserServiceImpl -- userServiceImpl
c. @Autowired -- 自动注入
可以标记在属性和set方法上,如果标记在属性上,可以没有set方法
特点:默认自动按照类型注入
流程:当属性|set方法标记了@Autowired ,会自动在容器中查询该属性类型的对象,如果有且只有一个,则注入
@Qualifier :必须与@Autowired结合使用
作用:如果自动注入按照类型注入失败,则按照指定的名称注入
如果没有@Qualifier,类型注入失败,则按照属性名按照名称注入
d. @Resource -- 自动注入
流程:当属性|set方法标记了@Resource,会自动按照名称注入, 如果名称没有找到,则根据类型注入,如果类型有多个,则抛出异常
e. @Autowired :默认按照类型注入,如果类型有多个,则按照名称注入 -- spring提供的
@Resource : 默认按照名称注入,没有名称没有找到,按照类型注入 -- jdk提供的
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
f. @Configuration : 标记该类为配置文件类
可以替换 applicationContext.xml
g. @ComponentSacn("com.torlesse")
相当于:<context:component-scan base-package="com.torlesse">
h. @Import: 引入其他配置文件
<import resource="classpath:applicationContext-dao.xml"></import>
i. @Bean -- 通过方法创建对象,一般用于创建别人提供的类
相当于:<bean >
j. @Scope("singleton|prototype")
配置对象的范围:相当于:bean标签中的属性 scope="singleton|prototype"
k. 生命周期
@PostConstruct:相当于bean标签的属性 init-method,指定初始化方法
@PreDestroy:相当于bean标签的属性:destroy-method, 指定对象的销毁方法
l. @Value 给属性赋值 -- 只能赋值简单类型
m. PropertySource : 引入外部属性文件
相当于:<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
三、使用注解改造账户CRUD
a、实体类
public class Account {
private Integer id;
private String name;
private Float money;
}
b、持久层
package com.torlesse.dao.impl;
import com.torlesse.dao.AccountDao;
import com.torlesse.domain.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;
/**
* dbUtils 操作数据库
* 入口:QueryRunner对象
*/
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
@Autowired
QueryRunner queryRunner;
@Override
public List<Account> findAll() {
String sql = "select * from account";
try {
List<Account> accountList = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));
return accountList;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public Account findById(Integer id) {
String sql = "select * from account where id = ?";
try {
Account account = queryRunner.query(sql, new BeanHandler<>(Account.class), id);
return account;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void save(Account account) {
String sql = "insert into account values(null , ? , ?)";
try {
queryRunner.update(sql ,account.getName() ,account.getMoney());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void update(Account account) {
String sql = "update account set name = ? ,money = ? where id = ?";
try {
queryRunner.update(sql ,account.getName(),account.getMoney(),account.getId());
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void del(Integer id) {
String sql = "delete from account where id = ?";
try {
queryRunner.update(sql ,id);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
c、业务层
package com.torlesse.service.impl;
import com.torlesse.dao.AccountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public List<Account> findAll() {
return accountDao.findAll();
}
@Override
public Account findById(Integer id) {
return accountDao.findById(id);
}
@Override
public void save(Account account) {
accountDao.save(account);
}
@Override
public void update(Account account) {
accountDao.update(account);
}
@Override
public void del(Integer id) {
accountDao.del(id);
}
}
d、配置文件
<?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: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/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解,扫描包-->
<context:component-scan base-package="com.torlesse"></context:component-scan>
<!--创建queryRunner对象, 需要数据源对象,创建数据源对象,通过构造方法注入-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<!--通过构造方法参数类型注入-->
<constructor-arg type="javax.sql.DataSource" ref="dataSource"></constructor-arg>
</bean>
<!--创建数据源对象:需要注入四个参数-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--通过set方法注入-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_331"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
</beans>
e、测试
package com.torlesse;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import com.torlesse.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* 1. 替换Junit的运行器: 为spring与junit整合后的运行器
* 2. 指定配置文件路径, 会自动创建容器对象
* @ContextConfiguration({"classpath:applicationContext.xml"})
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class TestCRUD {
// ApplicationContext ac;
// @Before
// public void init(){
// //创建容器
// ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//
// }
@Autowired
AccountService accountService;
@Test
public void testFindAll(){
//创建service对象
List<Account> accountList = accountService.findAll();
for (Account account : accountList) {
System.out.println(account);
}
}
@Test
public void testFindById(){
//创建service对象
Account account = accountService.findById(1);
System.out.println(account);
}
@Test
public void testSave(){
//创建service对象
AccountService accountService = new AccountServiceImpl();
Account account = new Account();
account.setName("ddd");
account.setMoney(1111F);
accountService.save(account);
}
@Test
public void testUpdate(){
//创建service对象
AccountService accountService = new AccountServiceImpl();
Account account = new Account();
account.setId(4);
account.setName("eeee");
account.setMoney(1222F);
accountService.update(account);
}
@Test
public void testDel(){
AccountService accountService = new AccountServiceImpl();
accountService.del(4);
}
}
四、纯注解开发
1. SpringConfiguration.java
/**
* 1. 标记该类为配置文件类 @Configuration
* 2. 指定注解扫描的包路径 @ComponentScan({"com.torlesse"})
* 3. 引入其他配置文件类 @Import({JDBCConfiguration.class})
*/
@Configuration
@ComponentScan({"com.torlesse"})
@Import({JDBCConfiguration.class})
public class SpringConfiguration {
}
2. JDBCConfiguration.java
/**
* 配置持久层的对象
* @Configuration :可以省略的
*
* @Bean("name") 用在方法上,用来指定方法创建的对象存到容器中
* "name": 就是在容器的名称
*/
@Configuration
public class JDBCConfiguration {
@Bean("queryRunner")
public QueryRunner createQueryRunner(DataSource dataSource){
// 需用通过构造方法注入dataSource
QueryRunner queryRunner = new QueryRunner(dataSource);
return queryRunner;
}
/**
* 创建数据源对象:dataSource
* @return
*/
@Bean("dataSource")
public DataSource createDataSource(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring_331");
dataSource.setUser("root");
dataSource.setPassword("root");
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
return dataSource;
}
}
3. 测试
//纯注解创建容器对象
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//创建service对象
AccountService accountService = ac.getBean("accountService",AccountService.class);
六、spring与junit的整合
1. 引入依赖
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--引入spring-5 的测试包: 必须引用相应的junit包, junit的版本必须是4.12以上-->
<!--引入spring-4 的测试包: 必须引用相应的junit包, junit的版本必须是4.9以上-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.1 配置测试环境-- xml
a. 替换Junit的运行器: 为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
b. 指定配置文件路径, 会自动创建容器对象, 必须添加classpath
@ContextConfiguration({"classpath:applicationContext.xml"})
2.2 配置测试环境-- ann
a. 替换Junit的运行器: 为spring与junit整合后的运行器
@RunWith(SpringJUnit4ClassRunner.class)
b. 指定配置文件路径, 会自动创建容器对象, 必须添加classpath
@ContextConfiguration(classes = {SpringConfiguration.class})
3. 测试:从容器可以获取某类型的对象
@Autowired
AccountService accountService;
Spring入门【三】
以转账为案例。
一、转账编码
1、引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
2、实体类
public class Account {
private Integer id;
private String name;
private Float money;
}
3、持久层
1. 接口
package com.torlesse.dao;
import com.torlesse.domain.Account;
public interface AcountDao {
/**
* 根据账户名查询账户
* @param fromName
* @return
*/
Account findByName(String fromName);
/**
* 更新账户
* @param fromAccount
*/
void update(Account fromAccount);
}
2. 实现类
package com.torlesse.dao.impl;
import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.sql.SQLException;
@Repository
public class AcountDaoImpl implements AcountDao {
@Autowired
QueryRunner queryRunner;
@Override
public Account findByName(String fromName) {
String sql = "select * from account where name = ?";
try {
return queryRunner.query(sql ,new BeanHandler<>(Account.class),fromName);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void update(Account account) {
String sql = "update account set money = ? where name = ?";
try {
queryRunner.update(sql ,account.getMoney(), account.getName());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4、业务层
1. 接口
package com.torlesse.service;
public interface AccountService {
/**
*
* @param fromName 从哪个账户转出
* @param toName 转入哪个账户
* @param money 转多少钱
*/
public void transfer(String fromName, String toName ,Float money);
}
2. 实现类
package com.torlesse.service.impl;
import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* 一个事务必须在一个Connection中完成
*
* ThreadLocal:线程绑定
* 绑定Connection对象
* 业务层和持久层需要Connection从ThreadLocal中获取
*
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AcountDao acountDao;
@Override
public void transfer(String fromName, String toName, Float money) {
try {
//事务1:开启事务:conn.setAutoCommit(false);
//查询要转出的账户
Account fromAccount = acountDao.findByName(fromName);
//查询转入的账户
Account toAccount = acountDao.findByName(toName);
//修改要转出的账户余额:假设余额充足
fromAccount.setMoney(fromAccount.getMoney() - money);
//修改要转入的账户余额
toAccount.setMoney(toAccount.getMoney() + money);
//持久化到数据库
acountDao.update(fromAccount);
//出现异常
System.out.println(1/0);
acountDao.update(toAccount);
//事务2:提交事务:conn.commit();
} catch (Exception e) {
//事务3:回顾事务 conn.rollback();
e.printStackTrace();
} finally {
//事务4:还原状态 conn.setAutoCommit(true);
}
System.out.println();
}
}
5、配置文件
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描表,创建对象-->
<context:component-scan base-package="com.torlesse"></context:component-scan>
<!--创建queryRunner对象:构造方法中需要DataSource对象-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
<constructor-arg type="javax.sql.DataSource" ref="dataSource"></constructor-arg>
</bean>
<!--创建dataSource对象:注入四个必要属性-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
</beans>
6、测试
package com.torlesse;
import com.torlesse.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransfer {
@Autowired
AccountService accountService;
@Test
public void test(){
//创建类对象,创建springIOC容器
// ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
accountService.transfer("aaa","bbb", 100f);
}
}
7、发现问题
二、解决转账问题
1、引入工具类
引入ConnectionUtils
package com.torlesse.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;
/**
* 一个管理连接的工具类,用于实现连接和线程的绑定
*
* 保证当前线程中获取的Connection是同一个
*/
@Component
public class ConnectionUtil {
private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
@Autowired
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 获取当前线程上绑定的连接
* @return
*/
public Connection getThreadConnection() {
try {
//1.先看看线程上是否绑了
Connection conn = tl.get();
if(conn == null) {
//2.从数据源中获取一个连接
conn = dataSource.getConnection();
//3.和线程局部变量绑定
tl.set(conn);
}
//4.返回线程上的连接
return tl.get();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 把连接和当前线程解绑
*/
public void remove() {
tl.remove();
}
}
引入TransactionManager
package com.torlesse.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/**
* 事务管理器
*/
@Component
public class TransactionManager {
@Autowired
private ConnectionUtil connectionUtil;
public void setConnectionUtil(ConnectionUtil connectionUtil) {
this.connectionUtil = connectionUtil;
}
//开启事务
public void beginTransaction() {
//从当前线程上获取连接,实现开启事务
try {
connectionUtil.getThreadConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事务
public void commit() {
try {
connectionUtil.getThreadConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚事务
public void rollback() {
try {
connectionUtil.getThreadConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//释放连接
public void release() {
try {
connectionUtil.getThreadConnection().setAutoCommit(true);
//关闭连接(还回池中)
connectionUtil.getThreadConnection().close();
//解绑线程:把连接和线程解绑
connectionUtil.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2、修改业务层
package com.torlesse.service.impl;
import com.torlesse.dao.AcountDao;
import com.torlesse.domain.Account;
import com.torlesse.service.AccountService;
import com.torlesse.utils.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
* 一个事务必须在一个Connection中完成
*
* ThreadLocal:线程绑定
* 绑定Connection对象
* 业务层和持久层需要Connection从ThreadLocal中获取
*
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AcountDao acountDao;
@Autowired
TransactionManager txManager;
/**
* 假设需要事务管理
* @param account
*/
public void update(Account account){
try {
//事务1:开启事务:conn.setAutoCommit(false);
txManager.beginTransaction();
acountDao.update(account);
//事务2:提交事务:conn.commit();
txManager.commit();
} catch (Exception e) {
//事务3:回顾事务 conn.rollback();
txManager.rollback();
e.printStackTrace();
} finally {
//事务4:还原状态 conn.setAutoCommit(true);
txManager.release();
}
}
@Override
public void transfer(String fromName, String toName, Float money) {
try {
//事务1:开启事务:conn.setAutoCommit(false);
txManager.beginTransaction();
//查询要转出的账户
Account fromAccount = acountDao.findByName(fromName);
//查询转入的账户
Account toAccount = acountDao.findByName(toName);
//修改要转出的账户余额:假设余额充足
fromAccount.setMoney(fromAccount.getMoney() - money);
//修改要转入的账户余额
toAccount.setMoney(toAccount.getMoney() + money);
//持久化到数据库
acountDao.update(fromAccount);
//出现异常
System.out.println(1/0);
acountDao.update(toAccount);
//事务2:提交事务:conn.commit();
txManager.commit();
} catch (Exception e) {
//事务3:回顾事务 conn.rollback();
txManager.rollback();
e.printStackTrace();
} finally {
//事务4:还原状态 conn.setAutoCommit(true);
txManager.release();
}
}
}
3、发现新的问题
问题
1. 重复代码
2. 代码臃肿问题
3. 技术与业务整合到一起了
解决的思路
1. 提取重复的代码
2. 业务层中不需要技术代码
3. 不修改业务层源码的情况下,技术增强
4. 使用动态代理
动态代理
特点:随用随创建,随用随加载
不修改原来代码的基础上,对原来的代码增强
三、动态代理
1、jdk动态代理
a. jdk动态代理: 基于接口的动态代理
b. @Test
public void testJDKProxy(){
//真实的对象
NewSale newSale = new NewSaleImpl();
//创建代理对象-- 本质上就是接口的一个实现类
//参数1: 类加载器
//参数2:类实现的接口
//参数3:真实对象的增强部分:实现了InvocationHandler接口的实现类
//匿名内部类也是该接口的实现类
NewSale sale = (NewSale) Proxy.newProxyInstance(newSale.getClass().getClassLoader(), newSale.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 增强内容
* @param proxy : 代理对象
* @param method : 代理的方法,未增强的方法
* @param args : 代理的方法的参数
* @return 代理的方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//生成产品
ProductFactory productFactory = new ProductFactory();
productFactory.make();
//开始销售:通过反射执行真实对象的方法
//参数1:真实的对象
//参数2:方法的参数
method.invoke(newSale,args );
//判断是否挣钱了
//卖的价格:args[0] 假设产品的成本是 1500
if( (Float)args[0] > 1500){
//卖的价格高于成本,挣了,可卖
System.out.println("卖的价格高于成本,挣了,可卖");
}else{
//卖的价格低于成本,赔了,不可卖
System.out.println("卖的价格低于成本,赔了,不可卖");
}
return null;
}
});
sale.sale(1000F);
}
2、cglib动态代理
a. cglib动态代理: 基于类的动态代理
b. 第三方jar包: cglib-2.2.2.jar
c. 注意:代理的类不用final修饰
d. public void testCglibProxy(){
//真实对象
OldSale oldSale = new OldSale();
//创建cglib代理对象
//1. 创建增强类对象
Enhancer enhancer = new Enhancer();
//2. 指定代理对象的父类
enhancer.setSuperclass(oldSale.getClass());
//3. 指定增强的内容
//MethodInterceptor :接口是方法拦截器
enhancer.setCallback(new MethodInterceptor() {
/***
* 增强的内容
* @param o 代理对象,增强后的对象
* @param method 代理的方法
* @param objects 代理的方法的参数
* @param methodProxy 代理方法,增强后的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//生成产品
ProductFactory factory = new ProductFactory();
factory.make();
//开始销售,执行真实对象的内容
method.invoke(oldSale, objects);
//判断是否挣钱了
//卖的价格:objects[0] 假设产品的成本是 1500
if( (Float)objects[0] > 1500){
//卖的价格高于成本,挣了,可卖
System.out.println("卖的价格高于成本,挣了,可卖");
}else{
//卖的价格低于成本,赔了,不可卖
System.out.println("卖的价格低于成本,赔了,不可卖");
}
return null;
}
});
//4. 创建代理对象:
OldSale saleProxy = (OldSale)enhancer.create();
saleProxy.sale(2000f);
}
五、动态代理解决新问题
1、jdk动态代理解决问题
@Test
public void testJDKProxyService(){
//创建业务层代理对象
AccountService accountServiceProxy = (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//开启事务
txManager.beginTransaction();
//执行真实的对象的方法
method.invoke(accountService, args);
//提交事务
txManager.commit();
} catch (Exception e) {
//事务回滚
txManager.rollback();
e.printStackTrace();
} finally {
//还原状态
txManager.release();
}
return null;
}
});
accountServiceProxy.transfer("aaa","bbb",200f);
}
2、cglib动态代理解决问题
@Test
public void testCglibProxy(){
//增强对象
Enhancer enhancer = new Enhancer();
//指定代理对象的父类
enhancer.setSuperclass(accountService.getClass());
//增强的内容
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
txManager.beginTransaction();
method.invoke(accountService, objects);
txManager.commit();
} catch (Exception e) {
txManager.rollback();
e.printStackTrace();
} finally {
txManager.release();
}
return null;
}
});
//创建代理对象
AccountService accountServiceProxy = (AccountService)enhancer.create();
accountServiceProxy.transfer("aaa","bbb",200f);
}
四、AOP的xml配置
1、依赖
<!--引入spring的核心-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--引入spring的测试包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--引入单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--配置aop,必须引入一个包:版本必须要1.8.7以上-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
2、配置文件
<?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:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--扫描表,创建bean对象-->
<context:component-scan base-package="com.torlesse"></context:component-scan>
<!--通知对象: 拦截到方法时,通知执行的对象-->
<!--
通知的类型
前置通知: 方法之前执行
后置通知: 方法执行完之后执行- 返回之前执行-如果有异常,则不执行
最终通知: 方法执行完后总会执行- finally
异常通知: 方法出现异常则执行
环绕通知: 前置通知+后置通知+最终通知+异常通知
-->
<bean id="logger" class="com.torlesse.log.Logger"></bean>
<!--配置aop-->
<aop:config>
<!--配置切面= 切入点 + 通知
指定通知对象是谁
-->
<aop:aspect ref="logger">
<!--配置切入点
id:唯一的标志
expression: 表达式
* com.torlesse.service.impl.*.*(..)
* com.torlesse.service..*.*(..)
第一个*:代表方法任意返回值类型
第二个*: 类名任意,impl包中所有的类
第三个*: 任意方法名
(..) : 参数任意,个数任意,类型任意,顺序任意
其他的配置方式
public void com.torlesse.service.impl.UserServiceImpl.findAll()
void com.torlesse.service.impl.UserServiceImpl.findAll()
* com.torlesse.service.impl.UserServiceImpl.findAll()
* com.torlesse.service..UserServiceImpl.findAll() : .. 代表的是包,及其子包
-->
<aop:pointcut id="pointcut" expression="execution(* com.torlesse.service.impl.*.*(..))"></aop:pointcut>
<!--织入: 告诉通知对象执行,具体执行哪一个方法-->
<!--前置通知-->
<!--<aop:before method="before" pointcut-ref="pointcut"></aop:before>-->
<!--后置通知-->
<!--<aop:after-returning method="afterReturning" pointcut-ref="pointcut"></aop:after-returning>-->
<!--最终通知-->
<!--<aop:after method="after" pointcut-ref="pointcut"></aop:after>-->
<!--异常通知-->
<!--<aop:after-throwing throwing="e" method="afterThrowing" pointcut-ref="pointcut"></aop:after-throwing>-->
<!--环绕增强-->
<aop:around method="around" pointcut-ref="pointcut"></aop:around>
</aop:aspect>
</aop:config>
</beans>
五、AOP的注解配置
package com.torlesse.log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
*@Component: 创建类对象
* @Aspect:配置该类为切面
* 切面是:切入点 + 通知
*
*/
@Component
@Aspect
public class Logger {
/**
* 配置切入点
*/
@Pointcut("execution(* com.torlesse.service.impl.*.*(..))")
public void pointcut(){};
/**
*
* @param joinPoint 连接点-- 拦截到的方法
*/
// @Before("pointcut()")
public void before(JoinPoint joinPoint){
//被代理的对象
Object target = joinPoint.getTarget();
//拦截的类的名称
String className = target.getClass().getName();
System.out.println("拦截到的类名:" +className);
//获取方法对象
Signature signature = joinPoint.getSignature();
//获取方法名
String methodName = signature.getName();
System.out.println("拦截到方法名:" + methodName);
System.out.println("前置通知");
}
// @AfterReturning("pointcut()")
public void afterReturning(){
System.out.println("后置增强");
}
// @After("pointcut()")
public void after(){
System.out.println("最终增强");
}
// @AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("执行的方法的异常:"+e);
System.out.println("异常通知");
}
/**
* ProceedingJoinPoint 可以执行拦截到方法的连接点对象
* @param joinPoint
*/
@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint){
try {
System.out.println("前置通知");
//执行原来的方法: 可以获取方法的返回值
Object result = joinPoint.proceed();
System.out.println("后置增强");
} catch (Throwable e) {
System.out.println("异常通知");
// e.printStackTrace();
} finally {
System.out.println("最终增强");
}
}
}
Spring入门【四】
一、jdbcTemplate的使用
1、jdbcTemplate的介绍
jdbc --- dbutils -- jdbcTemplate(spring 提供) -- mybatis(主流) -- spring data jpa(趋势)
2、数据源配置
a. c3p0数据源
<!--c3p0数据源-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置c3p0数据源-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
b. dbcp 数据源
<!--dbcp数据源-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!--dbcp数据源-->
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
c. spring jdbc 自带数据源,包含了JdbcTemplate对象
<!--spring自带数据源-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring自带的数据源-->
<bean id="springDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
3、jdbcTemplate的CRUD
package com.torlesse;
import com.torlesse.domain.Account;
import com.torlesse.mapper.AccountRowMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* 查询使用query
* 增删改:update
*
* queryForList :查询返回一个List集合,Map集合
* query(sql ,属性与列的映射对象,参数):返回值:List<pojo>
* queryForObject :针对于返回一个对象
* update(sql ,参数):执行增删改操作
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
public void testFindAll(){
String sql = "select * from account";
// 方法返回值是List集合,list中是map、集合
// List<Map<String, Object>> accountList = jdbcTemplate.queryForList(sql);
// for (Map<String, Object> map : accountList) {
// System.out.println(map);
// }
List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper());
for (Account account : accountList) {
System.out.println(account);
}
}
@Test
public void testFindById(){
String sql = "select * from account where id = ?";
// List<Account> accountList = jdbcTemplate.query(sql, new AccountRowMapper(), 5);
// System.out.println(accountList.size() == 1 ?accountList.get(0):"结果为null");
// queryForObject(SQL语句,列与属性的映射, 参数):掌握
Account account = jdbcTemplate.queryForObject(sql, new AccountRowMapper(), 5);
System.out.println(account);
// queryForObject(sql语句,参数列表(必须是数组类型), jdbcTemplate 自带映射:必须保证属性和列名一致)
// Account account = jdbcTemplate.queryForObject(sql, new Object[]{1}, new BeanPropertyRowMapper<>(Account.class));
// System.out.println(account);
}
@Test
public void testSave(){
String sql = "insert into account values(null , ? ,?)";
jdbcTemplate.update(sql ,"zhangsan", 10000);
}
@Test
public void testUpdate(){
String sql = "update account set money = ?, name = ? where id = ?";
jdbcTemplate.update(sql ,1000,"lisi",4);
}
@Test
public void testDel(){
String sql = "delete from account where id = ?";
jdbcTemplate.update(sql , 4);
}
@Test
public void testGetTotalCount(){
String sql = "select count(*) from account";
// queryForObject(sql语句, 返回值类型)
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println(count);
}
}
4、在dao中使用jdbcTemplate方法一
a. applicationContext.xml
<!--创建jdbcTemplate模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="springDataSource"></property>
</bean>
b.在dao层中使用模板对象
@Repository
public class AccountDaoImpl implements AccountDao {
//自动注入模板对象
@Autowired
JdbcTemplate jdbcTemplate;
}
5、在dao中使用jdbcTemplate方法二
a. 在dao实现类中继承接口JdbcDaoSupport类
public class AccountDaoImpl2 extends JdbcDaoSupport implements AccountDao {
b.所有的dao对象需要在xml中创建,需要通过set方法注入数据源对象
<!--创建dao层对象-->
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl2">
<!--通过set方法注入数据源-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
二、spring的事务控制
1、spring事务控制的api介绍–事务管理类
org.springframework.orm.hibernate5.HibernateTransactionManager: 在hibernate环境下使用
rg.springframework.jdbc.datasource.DataSourceTransactionManager: 在jdbcTemplate,mybatis(ibatis)环境下使用
2、事务的特性
a、事务的四个特性
原子性: 不可再分割
隔离性: 事务之间的隔离级别
一致性: 要么全部完成,要么全部不完成
持久性: 一旦提交持久化到数据中
b、隔离级别
读未提交:read uncommited
产生的问题:脏读,幻读,不可重复读
脏读:读到了未提交的数据
不可重复读:
幻读(虚读):
读已提交:read commited
产生的问题:幻读,不可重复读
解决的问题:脏读
重复读: repeatable read
产生的问题:幻读
解决的问题:脏读,不可重复读
串行化(序列化): serializable
产生的问题:null
解决的问题: 所有的问题
隔离级别最高,效率最低
c、数据库的支持的隔离级别 -- 一般情况下选择都是默认的隔离级别
mysql:支持:read uncommited read commited repeatable read serializable 支持三个隔离级别
默认的隔离级别:repeatable read
Oracle支持:read commited serializable read only(只读)
默认的隔离级别:read commited
3、事务的传播
a. 掌握
REQUIRED: 必要的: 如果没有事务,则新建一个事务,如果有事务,加入这个事务当中, spring指定为默认值
增删改:
SUPPORTS: 支持的: 如果没有事务,非事务执行,如果有事务,加入这个事务当中
查询
b. 了解
MANDATORY: 可以使用当前的事务,如果没有事务,抛出异常
REQUERS_NEW: 新建一个事务,如果存在事务,则挂起事务
NOT_SUPPORTED: 必须非事务执行,如果有事务,则挂起事务
NEVER: 非事务执行,如果存在事务,抛出异常
NESTED: 有事务,嵌套执行,没有事务,执行REQUIRED
4、是否为只读的事务
a.如果是查询,则为只读的事务 readOnly=true
b.如果是增删改,则为非只读的事务,readOnly=false=
5、基于xml声明式事务管理(配置文件)(重点)(推荐)
a. 编程式事务管理:在业务层写了事务技术代码
b. 声明式事务管理:在配置文件声明事务对象,管理事务,业务层中没有任何事务代码
1) 引入依赖:
<!--aop的切面配置需要的jar包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!--spring 的事务管理jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring 的基础包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--spring自带数据源, 包含了事务管理类 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--两个测试包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2) 编写配置文件
<!--创建事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
<!--事务的增强: 过滤方法是否需要拦截
id:唯一的标识
transaction-manager:指定事务管理器
-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--增强:方法的过滤-->
<tx:attributes>
<!--指定需要拦截的方法
isolation:隔离级别, 一般选择默认的
propagation:传播的行为,
read-only: 是否为只读的事务,增删改:非只读事务 read-only=false
查询:只读的事务:read-only=true
find* :通配符配置,只要以 find开头即可
-->
<!--配置方式一:-->
<!--增删改-->
<!--<tx:method name="insert*" />-->
<!--<tx:method name="add*" />-->
<!--<tx:method name="update*" />-->
<!--<tx:method name="del*" />-->
<!--<tx:method name="delete*" />-->
<!--<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>-->
<!--查询-->
<!--<tx:method name="find*" propagation="SUPPORTS" read-only="true" ></tx:method>-->
<!--<tx:method name="select*" propagation="SUPPORTS" read-only="true" ></tx:method>-->
<!--<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>-->
<!--<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"></tx:method>-->
<!--配置方式二-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>
<!--其他方法的配置方法-->
<tx:method name="*" propagation="REQUIRED" read-only="false"></tx:method>
</tx:attributes>
</tx:advice>
<!--aop的配置:
切面= 切入点 +通知(增强)
-->
<aop:config>
<!--切面配置
advice-ref: 通知关联对象
pointcut: 切入点:
-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.torlesse.service.impl.*.*(..))"></aop:advisor>
</aop:config>
3) 业务层
业务层不需要任何事务管理,只需要提供业务代码即可
6、基于注解的配置(重点)
a. 引入依赖
与xml完全一致
b. 配置文件
1)配置事务管理类
<!--创建事务管理器对象-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源属性:事务存在连接中- 连接存在连接池(数据源)中-->
<property name="dataSource" ref="springDataSource"></property>
</bean>
2) 开启事务的注解管理
<!--transaction-manager: 关联事务管理器对象-->
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
<!--开启aop的注解:自动代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
c. 业务层
在类上标记注解:@Transactional, 类中所有的方法都会使用事务管理
在方法上标记注解:@Transactional:只有该方法按照事务执行
d.属性介绍
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false,timeout = -1)
书籍推荐
以下是一些关于Spring框架的书籍推荐:
- 《Spring揭秘》:这本书是一本经典的Spring入门书籍,适合想要深入了解Spring框架的开发者。
- 《精通Spring 4.x》:这本书以实战为主,通过实例讲解了Spring框架的各种组件,适合想要快速掌握Spring框架的开发者。
- 《Spring Boot实战》:这本书介绍了如何使用Spring Boot快速构建Web应用程序,适合想要学习如何使用Spring Boot开发的开发者。
- 《Spring核心技术》:这本书系统地介绍了Spring框架各个基本组件的基本使用,适合想要全面了解Spring框架的开发者。
希望对你有所帮助~