文章目录
Spring
介绍
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
优势、特点
1.方便解耦,简化开发
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP 编程的支持
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
3.声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5.方便集成各种优秀框架
Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、HessianQuartz
等)的直接支持。
6.降低 JavaEE API 的使用难度
Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
7.Java 源码是经典学习范例
Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
Spring的体系结构、组成模块
Spring框架包含的功能大约由20个小模块组成。这些模块按组可分为核心容器(Core Container)、数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP和Aspects)、设备(Instrumentation)、消息(Messaging)和测试(Test)。
1.核心容器
1.spring-core:该模块是依赖注入IoC与DI的最基本实现。
2.spring-beans:该模块是Bean工厂与bean的装配。
3.spring-context:该模块构架于核心模块之上,它扩展了 BeanFactory,为它添加了 Bean 生命周期控制、框架事件体系以及资源加载透明化等功能。ApplicationContext 是该模块的核心接口,它的超类是 BeanFactory。与BeanFactory 不同,ApplicationContext 容器实例化后会自动对所有的单实例 Bean 进行实例化与依赖关系的装配,使之处于待用状态。
4.spring-context-indexer:该模块是 Spring 的类管理组件和 Classpath 扫描。
5.spring-context-support:该模块是对 Spring IOC 容器的扩展支持,以及 IOC 子容器。
6.spring-expression:该模块是Spring表达式语言块是统一表达式语言(EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等。
2.数据访问/集成
1.spring-jdbc:该模块提供了 JDBC抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
2.spring-tx:该模块支持编程式事务和声明式事务,可用于实现了特定接口的类和所有的 POJO 对象。编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细。
3.spring-orm:该模块提供了对流行的对象关系映射 API的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring 的其它功能整合,比如前面提及的事务管理。
4.spring-oxm:该模块提供了对 OXM 实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
5.spring-jms:该模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了 spring-messaging 模块。
3.Web
1.spring-web:该模块为 Spring 提供了最基础 Web 支持,主要建立于核心容器之上,通过 Servlet 或者 Listeners 来初始化 IOC 容器,也包含一些与 Web 相关的支持。
2.spring-webmvc:该模块众所周知是一个的 Web-Servlet 模块,实现了 Spring MVC(model-view-Controller)的 Web 应用。
3.spring-websocket:该模块主要是与 Web 前端的全双工通讯的协议。
4.spring-webflux:该模块是一个新的非堵塞函数式 Reactive Web 框架,可以用来建立异步的,非阻塞,事件驱动的服务,并且扩展性非常好。
4.面向切面编程——AOP,Aspects
1.spring-aop:该模块是Spring的另一个核心模块,是 AOP 主要的实现模块。
2.spring-aspects:该模块提供了对 AspectJ 的集成,主要是为 Spring AOP提供多种 AOP 实现方法,如前置方法后置方法等。
5.设备(Instrumentation)
spring-instrument:该模块是基于JAVA SE 中的"java.lang.instrument"进行设计的,应该算是 AOP的一个支援模块,主要作用是在 JVM 启用时,生成一个代理类,程序员通过代理类在运行时修改类的字节,从而改变一个类的功能,实现 AOP 的功能。
6.消息(Messaging)
spring-messaging:该模块是从 Spring4 开始新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。
7.测试(Test)
spring-test:该模块主要为测试提供支持的,通过 JUnit 和 TestNG 组件支持单元测试和集成测试。它提供了一致性地加载和缓存 Spring 上下文,也提供了用于单独测试代码的模拟对象(mock object)。
内聚和耦合
内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
内聚
低—>高
1.偶然内聚:指一个模块内的各处理元素之间没有任何联系。
2.逻辑内聚:指模块内执行若干个逻辑上相似的功能,通过参数确定该模块完成哪一个功能。(类似于完成加法运算,有多个加法运算代码块,分别处理参数为int或float或double等。这些代码块之所以聚在一起,只是它们都是为了完成加法运算而已)
3.时间内聚:指把需要同时执行的动作组合在一起形成的模块。(类似于利用抽象工厂模式生成一碗粥,你可以先放水,也可以先放米,这两个动作之间没有必然的顺序,但为了生成一碗粥,需要同时执行这两个动作)
4.过程内聚:指一个模块完成多个任务,这些任务必须按照指定的过程执行。(类似于利用原生JDBC操纵数据库。你需要先连接JDBC获得connection对象,然后才能创建Statement对象,最后才能执行sql语句)
5.通信内聚:指模块内的所有处理元素都在同一个数据结构上操作,或者各处理使用相同的输入数据或者产生相同的输出数据。(类似于有一个数组,你只把它作为你遍历数组,增加数组节点,删除数组节点的参数)
6.顺序内聚:指一个模块中的各个处理元素都密切相关于同一功能且必须顺序执行,前一功能元素的输出就是下一功能元素的输入。(类似于程序模拟车间生产的某条流水线)
7.功能内聚:指模块内的所有元素共同作用完成一个功能,缺一不可。
耦合
高—>低
1.内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
2.公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
3. 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
4 .控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
5.标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间存在一个标记耦合。
6. 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形
式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
7. 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
IOC
配置文件实现
1.在pom.xml文件中导入依赖
<dependencies>
<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.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
</dependencies>
2.创建业务层接口和实现类、创建持久层接口和实现类
public interface UserService {
int saveUser(String name,String pwd);
}
public class UserServiceImpl implements UserService {
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public int saveUser(String name, String pwd) throws SQLException {
System.out.println("service.....save..........");
return userDao.saveUser(name,pwd);
}
}
public interface UserDao {
int saveUser(String name, String pwd);
}
public class UserDaoImpl implements UserDao {
DataSource dataSource;
@Override
public int saveUser(String name, String pwd) {
return 1;
}
}
3.xml文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.zbdx.pro.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.zbdx.pro.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
4.使用
ApplicationContext ac;
@Before
public void init(){
ac=new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void f1() {
UserService userService= (UserService) ac.getBean("userService");
userService.saveUser("aaa","123");
}
BeanFactory 和 ApplicationContext 的区别
BeanFactory 才是 Spring 容器中的顶层接口。
ApplicationContext 是它的子接口。
BeanFactory 和 ApplicationContext 的区别:
创建对象的时间点不一样。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么时候使用什么时候创建对象。
Bean标签
作用
用于配置对象让 spring 来创建的。
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
属性
id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。
singleton :默认值,单例的.
prototype :多例的.
request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.
init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称。
bean 的作用范围和生命周期
单例对象:scope="singleton"
一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
多例对象:scope="prototype"
每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了
实例化 Bean 的三种方式
第一种方式:使用默认无参构造函数
在默认情况下:它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。
public class User {
private int age;
private String name;
//提供get,set方法
public User() {}
}
<bean id="user" class="com.zbdx.pro.pojo.User"></bean>
ApplicationContext ac;
@Before
public void init(){
ac=new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void f2(){
User user = (User) ac.getBean("user");
System.out.println(user);
}
//输出:User{age=0, name='null'}
第二种方式:spring 管理静态工厂-使用静态工厂的方法创建对象
使用 DruidDataSourceFactory 类中的静态方法 getDataSource创建对象,并存入 spring 容器
id 属性:指定 bean 的 id,用于从容器中获取
class 属性:指定静态工厂的全限定类名
factory-method 属性:指定生产对象的静态方法
public class DruidDataSourceFactory {
// @Bean("dataSource")
public static DruidDataSource getDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setUrl("jdbc:mysql:///ww");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setPassword("lzj20010115");
dataSource.setUsername("root");
return dataSource;
}
}
<bean id="dataSource" class="com.zbdx.pro.DruidDataSourceFactory" factory-method="getDataSource"></bean>
@Test
public void f4() throws SQLException {
DataSource dataSource = (DataSource) ac.getBean("dataSource");
System.out.println(dataSource);
/** {CreateTime:"2023-07-02 22:16:11",ActiveCount:0,PoolingCount:0,
CreateCount:0,DestroyCount:0,CloseCount:0,ConnectCount:0,Connections:[]}*/
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from user");
ResultSet set = statement.executeQuery();
while (set.next()){
System.out.println(set.getString(2));
}
}
第三种方式:spring 管理实例工厂-使用实例工厂的方法创建对象
先把工厂的创建交给 spring 来管理。然后在使用工厂的 bean 来调用里面的方法。
factory-bean 属性:用于指定实例工厂 bean 的 id。
factory-method 属性:用于指定实例工厂中创建对象的方法。
public class DruidDataSourceFactory1 {
// @Bean("dataSource")
public DruidDataSource getDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setUrl("jdbc:mysql:///ww");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setPassword("lzj20010115");
dataSource.setUsername("root");
return dataSource;
}
}
<bean id="dataSourceFactory1" class="com.zbdx.pro.DruidDataSourceFactory1"></bean>
<bean id="dataSource1" factory-bean="dataSourceFactory1" factory-method="getDataSource"></bean>
@Test
public void f4() throws SQLException {
DataSource dataSource = (DataSource) ac.getBean("dataSource1");
System.out.println(dataSource);
/** {CreateTime:"2023-07-02 22:16:11",ActiveCount:0,PoolingCount:0,
CreateCount:0,DestroyCount:0,CloseCount:0,ConnectCount:0,Connections:[]}*/
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from user");
ResultSet set = statement.executeQuery();
while (set.next()){
System.out.println(set.getString(2));
}
}
spring 的依赖注入
依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。
ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
构造函数注入
就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置
的方式,让 spring 框架来为我们注入。
要求:类中需要提供一个对应参数列表的构造函数。
涉及的标签:
constructor-arg
属性:
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称 用这个找给谁赋值
==上面三个都是找给谁赋值,下面两个指的是赋什么值的==
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
public class User {
private int age;
private String name;
//提供全参构造方法,和toString方法
}
<bean id="user" class="com.zbdx.pro.pojo.User">
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="name" value="张三"></constructor-arg>
</bean>
@Before
public void init(){
ac=new ClassPathXmlApplicationContext("applicationContext.xml");
}
public void f2(){
User user = (User) ac.getBean("user");
System.out.println(user);
//User{age=18, name='张三'}
}
set 方法注入
就是在类中提供需要注入成员的 set 方法。
通过配置文件给 bean 中的属性传值:使用 set 方法的方式
涉及的标签:
property
属性:
name:找的是类中 set 方法后面的部分
ref:给属性赋值是其他 bean 类型的
value:给属性赋值是基本数据类型和 string 类型的
public class Student {
private int age;
private String name;
private double[] score;
//提供get、set方法,和toString方法
}
<bean id="student" class="com.zbdx.pro.pojo.Student">
<property name="name" value="李四"></property>
<property name="age" value="18"></property>
<property name="score" >
<set>
<value>99</value>
<value>88</value>
<value>77</value>
</set>
</property>
</bean>
ApplicationContext ac;
@Before
public void init(){
ac=new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void f3(){
Student student = (Student) ac.getBean("student");
System.out.println(student);
}
//Student{age=18, name='李四', score=[99.0, 88.0, 77.0]}
注解方式实现
1.导入依赖
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
2.创建类和数据库连接池
@Configuration
@Import(JdbcTemplateFactory.class)
@ComponentScan({"com.zbdx.pro.service","com.zbdx.pro.dao","com.zbdx.pro.pojo","com.zbdx.pro.factory"}) //包路径
public class SpringConfig {
}
public class User implements Serializable {
private int uid;
private String uname;
private String password;
//提供get,set方法,和toString方法
}
public interface UserDao {
List<User> findAll();
}
@Repository(value = "userDao")
public class UserDaoImpl implements UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public List<User> findAll() {
List<User> users = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<>(User.class));
return users;
}
}
public interface UserService {
List<User> findAll();
}
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public List<User> findAll() {
return userDao.findAll();
}
}
public class JdbcTemplateFactory {
@Value("${name}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${url}")
private String url;
@Value("${driver}")
private String driver;
@Bean(value = "jdbcTemplate")
public JdbcTemplate getTemplate(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(user);
dataSource.setPassword(pwd);
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
}
//db.properties配置文件
name:root
pwd:root
url:jdbc:mysql:///zbdx
driver:com.mysql.jdbc.Driver
3.使用
@ContextConfiguration(classes = SpringConfig.class)
public class Demo1 {
@Autowired
UserService userService;
@Test
public void testFindAll(){
userService.findAll().stream().forEach(u-> System.out.println(u));
/**User{id=1, name='qq', password='123'}User{id=2, name='ww', password='234'}User{id=3, name='ee', password='456'}*/
}
}
常用注解
@Component
作用:把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
属性:
value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
不是三大类型时使用
@Controller @Service @Repository
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。
@Autowired
作用:自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。
当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
@Autowired
UserDao userDao;
当有不止一个UserDao时,Spring不知道注入那个,就用userDao做为id去匹配
@Qualifier
作用:在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。
它在给字段注入时不能独立使用,必须和@Autowire 一起使用;
但是给方法参数注入时,可以独立使用。
属性:value:指定 bean 的 id。
@Autowired
@Qualifier("userDao2")
UserDao userDao;
@Resource
作用:直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
属性:name:指定 bean 的 id。
@Resource(name = "userDao2")
UserDao userDao;
@Value
作用:注入基本数据类型和 String 类型数据的
属性:value:用于指定值
public class Student implements Serializable {
@Value("张三")
private String name;
@Value("123")
private String pwd;
//提供get,set方法,和toString方法
}
@Scope
作用:指定 bean 的作用范围。
属性:value:指定范围的值。取值:singleton prototype
一般不使用
@Configuration
作用:用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。
获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class)。
属性:value:用于指定配置类的字节码
@Configuration
public class SpringConfig {}
@ContextConfiguration(classes = SpringConfig.class)
@ComponentScan
作用:用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:
<context:component-scan base-package=“com”/>是一样的。
属性:basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
@Configuration
@ComponentScan({"com.zbdx.pro.service","com.zbdx.pro.dao","com.zbdx.pro.pojo","com.zbdx.pro.factory"})
public class SpringConfig {}
@Bean
作用:该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
属性:name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
//连接数据库连接池
@Bean(value = "jdbcTemplate")
public JdbcTemplate getTemplate(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(user);
dataSource.setPassword(pwd);
dataSource.setUrl(url);
dataSource.setDriverClassName(driver);
JdbcTemplate template = new JdbcTemplate(dataSource);
return template;
}
@PropertySource
作用:用于加载.properties 文件中的配置。
例如我们配置数据源时,可以把连接数据库的信息写到properties 配置文件中,
就可以使用此注解指定 properties 配置文件的位置。
属性:value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:
@PropertySource("db.properties")
public class JdbcTemplateFactory {
@Value("${name}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${url}")
private String url;
@Value("${driver}")
private String driver;
}
db.properties文件,是配置文件可以放在resource,这样可以不加classpath
name:root
pwd:lzj20010115
url:jdbc:mysql:///ww
driver:com.mysql.jdbc.Driver
@Import
作用:用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。
属性:value[]:用于指定其他配置类的字节码。
@Configuration
@Import(JdbcTemplateFactory.class)
public class SpringConfig {}
关于 Spring 注解和 XML 的选择问题
注解的优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML 的优势:修改时,不用改源码。不涉及重新编译和部署。
基于xML配置 | 基于注解配置 | |
---|---|---|
Bean定义 | <bean id=“…” class=“…”/ > | @Component衔生类@Repository@Service @Controller |
Bean名称 | 通过id或name指定 | @Component(“person”) |
Bean注入 | < property>或者通过p命名空间 | @Autowired按类型注入@Qualifier按名称注入 |
@Autowired按类型注入@Qualifier按名称注入 | init-method destroy-method范围scope属性 | @PostConstruct初始化@PreDestroy销毁@Scope设置作用范围 |
适合场景 | Bean来自第三方,使用其它 | Bean的实现类由用户自己开发 |
AOP
1.Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
2.Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
3.Aspect(切面):是切入点和通知(引介)的结合。
4.Advice(通知):规定AOP执行的时机和执行的方法。a)前置通知b)后置通知c)抛出异常之后通知d)返回数据之后通知e)环绕通知
配置文件实现
1.导入依赖
<dependencies>
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
2.写类
@Data
@ToString
public class Account implements Serializable {
private int id;
private String name;
private double balance;
}
public interface AccountBiz {
void transfer(String sourceName,String targetName,double money);
}
public class AccountBizImpl implements AccountBiz {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String sourceName, String targetName, double money) {
Account source=accountDao.findAccountByName(sourceName);
Account target=accountDao.findAccountByName(targetName);
source.setBalance(source.getBalance()-money);
target.setBalance(target.getBalance()+money);
accountDao.update(source);
accountDao.update(target);
}
}
public interface AccountDao {
Account findAccountByName(String sourceName);
void update(Account target);
}
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Account findAccountByName(String sourceName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name =?", new BeanPropertyRowMapper<>(Account.class), sourceName);
return accounts.size()==0?null:accounts.get(0);
}
@Override
public void update(Account account) {
jdbcTemplate.update("update account set balance =? where id=?",account.getBalance(),account.getId());
}
}
3.配置文件
//db.properties
url:jdbc:mysql:///lll
driver:com.mysql.jdbc.Driver
user:root
pwd:lzj20010115
<?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:tx="http://www.springframework.org/schema/tx"
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/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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${user}"></property>
<property name="password" value="${pwd}"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountDao" class="com.zbdx.pro.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="accountBiz" class="com.zbdx.pro.biz.impl.AccountBizImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
4.使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccoutBizTest {
@Autowired
private AccountBiz accountBiz;
@Test
public void testTransfer(){
accountBiz.transfer("张三","李四",500);
}
}
这样正常张三账户减少500,李四增加500,但是默认每一条SQL语句都是一个事务,如果在张三账户减少500后,发生异常两个语句不在一个事务里,不会回滚,李四账户不会增加,但张三减少了,所以我们要找到切点,配置切点、切面和配置事务。
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务通知-->
<tx:advice id="ad" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS"></tx:method>
<tx:method name="get*" propagation="SUPPORTS"></tx:method>
</tx:attributes>
</tx:advice>
<!--配置切入点-->
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.zbdx.pro.biz.impl.*.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="ad" pointcut-ref="pt"></aop:advisor>
</aop:config>
注解实现
1.导入依赖,与上面相同
2.写类
@Data
@ToString
public class Account implements Serializable {
private int id;
private String name;
private double balance;
}
public interface AccountDao {
Account findAccountByName(String sourceName);
void update(Account target);
}
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findAccountByName(String sourceName) {
List<Account> accounts = jdbcTemplate.query("select * from account where name =?", new BeanPropertyRowMapper<>(Account.class), sourceName);
return accounts.size()==0?null:accounts.get(0);
}
@Override
public void update(Account account) {
jdbcTemplate.update("update account set balance =? where id=?",account.getBalance(),account.getId());
}
}
public interface AccountBiz {
void transfer(String sourceName,String targetName,double money);
}
@Service
@Transactional //开启事务
public class AccountBizImpl implements AccountBiz {
@Autowired
private AccountDao accountDao;
@Override
public void transfer(String sourceName, String targetName, double money) {
Account source=accountDao.findAccountByName(sourceName);
Account target=accountDao.findAccountByName(targetName);
source.setBalance(source.getBalance()-money);
target.setBalance(target.getBalance()+money);
accountDao.update(source);
accountDao.update(target);
}
}
3.写配置类
@Configuration
@ComponentScan("com.zbdx.pro")
@PropertySource("db.properties")
//url:jdbc:mysql:///lll driver:com.mysql.jdbc.Driver user:root pwd:lzj20010115
@EnableTransactionManagement //开启事务支持
public class SpringConfig {
@Value("${user}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${url}")
private String url;
@Value("${driver}")
private String driver;
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setPassword(pwd);
dataSource.setUsername(user);
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
return dataSource;
}
@Bean
public JdbcTemplate getJdbcTemplate(){
JdbcTemplate jdbcTemplate=new JdbcTemplate(getDataSource());
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager getManager(){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(getDataSource());
return manager;
}
}
4.使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccoutBizTest {
@Autowired
private AccountBiz accountBiz;
@Test
public void testTransfer(){
accountBiz.transfer("张三","李四",500);
}
}