学习概述
spring注入对象bean(IOC)
speing动态(静态)代理类(AOP)
spring声明事物
搭建spring环境
在maven中导入
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!-- spring包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!-- 测试用的包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
第一个spring程序helloworld
项目结构
java类HelloSpring
package com;
public class HelloSpring {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "helloSpring{" +
"str='" + str + '\'' +
'}';
}
}
配置文件beans.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="helloSpring" class="com.HelloSpring">
<property name="str" value="hello world"/>
</bean>
</beans>
测试类TestClass
package com;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestClass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
HelloSpring helloSpring = (HelloSpring) context.getBean("helloSpring");
System.out.println(helloSpring);
}
}
控制反转 (IoC)
IoC 也称为依赖注入 (DI)。这是一个过程,对象仅通过构造函数参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后设置的属性来定义它们的依赖关系(即与它们一起工作的其他对象) . 然后容器在创建 bean 时注入这些依赖项。
这个过程基本上是 bean 本身通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖关系的实例化或位置的逆过程(因此称为控制反转)。
简单点的理解方式:
通过使用一个容器进行对象的注入,在代码中编写对象时,无需将代码写死,而是在容器中进行对象的注入,这样只需要对容器操作即可控制代码中的对象,无需修改代码
bean
bean的功能类似于java中的new关键字,它的定义是一个创建一个或者多个对象的方法
<bean>元素中的属性如下:
- id:Bean的唯一标识符,Spring对Bean的配置、管理都通过该属性来完成;
- name:Spring同样可以通过name对Bean进行配置和管理,name属性可以为Bean定义多个名称,每个名称以逗号隔开;
- class:该属性指定了Bean的具体实现类,必须是一个完成的类名,使用类的全限定名;
- scope:设定Bean实例的作用域,其属性有singleton(单例)、prototype(原型)、request、session、和global Session,默认值为singleton,该属性会在下一篇博客中详细讲解;
- constructor-arg:<bean>元素的子元素,可以使用此元素传入构造参数进行实例化(上一篇博客的最后补充就是使用此属性进行实例化的),该元素的index属性指定构造参数的序号(从0开始);
- property:<bean>元素的子元素,通过调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入;
- ref:property、constructor-arg等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用;
- value:property、constructor-arg等元素的子元素,用来直接指定一个常量值;
- list:用于封装List或数组类型的依赖注入;
- set:用于封装Set或数组类型的依赖注入;
- map:用于封装Map或数组类型的依赖注入;
- entry:map元素的子元素,用于设定一个键值对,其key属性指定字符串类型的键值,ref或value子元素指定其值。
bean的实例化
在使用bean进行依赖项注入(创建)前,我们需要先对bean进行实例化,bean的实例化有三种方式:构造器实例化、静态工厂方式实例化、实例工厂方式实例化
构造器实例化
在xml文件中定义id和class两个属性即可
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
静态工厂方式实例化
该方式首先要求创建一个静态工厂类,然后在类中定义一个静态方法来创建Bean实例,静态工厂类及静态方法的代码如下:
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
然后是xml配置文件的内容如下:
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
实例工厂方式实例化
该种方式的工厂类中,不再使用静态方法创建Bean实例,而是采用直接创建Bean实例的方式。同时在配置文件中,需要实例化的Bean也不是通过class属性直接指向其实例化的类,而是通过factory-bean属性配置一个实例工厂,然后使用factory-method属性确定使用工厂中哪个方法。
工厂类方法代码如下:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
xml配置文件如下:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
bean的依赖项注入(参数注入)
基于有参构造函数注入(constructor-arg)
对于参数是对象的构造函数,通过constructor-arg元素的ref参数注入
<beans> <bean id="beanOne" class="x.y.ThingOne"> <constructor-arg ref="beanTwo"/> <constructor-arg ref="beanThree"/> </bean> <bean id="beanTwo" class="x.y.ThingTwo"/> <bean id="beanThree" class="x.y.ThingThree"/> </beans>
对于参数是简单参数类型,通过类型type,索引index(索引从0开始),名字name参数注入(优先级依次减低)
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
基于set函数注入(property)
基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数
static
工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。
<bean id="Addr" class="com.Addr">
<property name="addr" value="洛城"/>
</bean>
<bean id="Student" class="com.Student">
<!-- String name; -->
<property name="name" value="吕树"/>
<!-- Addr addr; -->
<property name="addr" ref="Addr"/>
<!-- String[] books; -->
<property name="books">
<array>
<value>数学</value>
<value>物理</value>
<value>化学</value>
</array>
</property>
<!-- List<String> habit; -->
<property name="habit">
<list>
<value>犯贱</value>
<value>收集负面情绪</value>
</list>
</property>
<!-- Map<String,String> caed; -->
<property name="caed">
<map>
<entry key="身份证" value="111111111111111111"/>
<entry key="学生证" value="123464845611465484"/>
</map>
</property>
<!-- Set<String> game; -->
<property name="game">
<set>
<value>飞剑</value>
<value>枪</value>
</set>
</property>
<!-- String wife; 空值注入 -->
<property name="wife">
<null/>
</property>
<!-- Properties info; -->
<property name="info">
<props>
<prop key="drive">546489</prop>
<prop key="url">1564</prop>
</props>
</property>
</bean>
bean的作用域
(默认)将单个 bean 定义限定为每个 Spring IoC 容器的单个对象实例。 | |
将单个 bean 定义限定为任意数量的对象实例。 | |
将单个 bean 定义限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该实例是在单个 bean 定义的后面创建的。仅在 Web 感知 Spring 的上下文中有效 | |
将单个 bean 定义限定为 HTTP 的生命周期 | |
将单个 bean 定义限定为 | |
将单个 bean 定义限定为 |
bean的自动装配
可以在bean标签中设置autowire的值实现自动装配
byName自动装配
byType自动装配
使用byName时,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
使用byType时,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
使用注解注入
在spring中使用注解注入必须使用下面代码在xml文件中导入约束
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 指定要扫描的包,该包下的所有注解都生效--> <context:component-scan base-package="包完整路径"/> <context:annotation-config/> </beans>
@Component(组件),将该注解写在类名的上一行,表面该类已经被spring管理了,成为了bean
@Component public class Dog { public void xwei(){ System.out.println("汪汪汪~~"); } }
@Resource,将该注解写在属性字段,有参构造函数或者set方法上可以实现对象注入,在bean中查找注入,@Resource先通过byName方式在容器中查找bean,当找不到name时,通过byType方式查找装配,都找不到就报错。
@value,将注解写在属性前,可实现通过外部资源文件注入简单属性
@Component public class MovieRecommender { private final String catalog; public MovieRecommender(@Value("${catalog.name}") String catalog) { this.catalog = catalog; } }
必须有如下java配置类
@Configuration @PropertySource("classpath:application.properties") public class AppConfig { }
以及以下
application.properties
文件:catalog.name=MovieCatalog
使用注解自动装配
在属性字段上使用@Autowired
@Autowired(required=false) //required为可选值(默认为true),当定义 private Dog dog; //为false时,表明对象值可以为null,否则报错
@Autowired先通过byType方式在容器中查找bean,当有多个相同类型时,通过byName方式查找装配属性名(例子中的dog)和bean的id值或者name。
可以通过使用@Qualifier(value="别名")来指定别名,通过别名来匹配bean的id值或者name值
@Autowired(required=false) @Qualifier(value="别名") private Dog dog;
在属性字段上使用@Resource(name="别名")
@Resource(name="别名") //name为可选值(默认为属性名) private Dog dog;
@Resource先通过byName方式在容器中查找bean,当找不到name时,通过byType方式查找装配,都找不到就报错。
speing动态(静态)代理类(AOP)( 面向切面编程)
AOP基于反射实现
Aspect:跨多个类的关注点的模块化。事务管理是企业 Java 应用程序中横切关注点的一个很好的例子。在 Spring AOP 中,方面是通过使用常规类(基于模式的方法)或使用
@Aspect
注解注释的常规类(@AspectJ 样式)来实现的。Join point:程序执行过程中的一个点,例如方法的执行或异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
Advice:方面在特定连接点采取的行动。不同类型的建议包括“around”、“before”和“after”advice。(Advice类型将在后面讨论。)包括 Spring 在内的许多 AOP 框架将Advice建模为interceptor ,并在连接点周围维护一个join point。
Pointcut:匹配连接点的谓词。Advice 与切入点表达式相关联,并在与切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。切入点表达式匹配的连接点的概念是 AOP 的核心,Spring 默认使用 AspectJ 切入点表达式语言。
Introduction:代表一个类型声明额外的方法或字段。Spring AOP 允许您向任何建议的对象引入新接口(和相应的实现)。例如,您可以使用介绍使 bean 实现
IsModified
接口,以简化缓存。(介绍在 AspectJ 社区中称为类型间声明。)Target object:一个或多个方面建议的对象。也称为“建议对象”。由于 Spring AOP 是使用运行时代理实现的,因此该对象始终是代理对象。
AOP proxy:由 AOP 框架创建的对象,用于实现方面协定(建议方法执行等)。在 Spring Framework 中,AOP 代理是 JDK 动态代理或 CGLIB 代理。
Weaving:将方面与其他应用程序类型或对象链接以创建建议对象。这可以在编译时(例如,使用 AspectJ 编译器)、加载时或运行时完成。Spring AOP 与其他纯 Java AOP 框架一样,在运行时执行编织。
Spring AOP 包括以下类型的通知:
Before advice:在连接点之前运行但不能阻止执行流继续到连接点的通知(除非它抛出异常)。
After returning advice:在连接点正常完成后运行的通知(例如,如果方法返回而没有引发异常)。
After throwing advice:如果方法因抛出异常而退出,则运行建议。
After (finally) advice::无论连接点以何种方式退出(正常或异常返回),都将运行建议。
Around advice:围绕连接点的建议,例如方法调用。这是最有力的建议。环绕通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点还是通过返回自己的返回值或抛出异常来缩短建议的方法执行。
为了便于理解,实例使用日志类作为
在一个类使用前后加一个日志功能,而且不改变源码,只会增加新的类
新加一个实现特定api接口的类(例子中为AfterReturningAdvice)
package com.aop; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class ReturnA implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+"类的"+method.getName()+"方法结束,返回值为: "+returnValue); } }
returnValue为所需要附加日志的类的返回值类型
method为需要附加日志的类的当前运行方法
args为需要附加日志的类的参数列表
target需要附加日志的类的本体
xml配置
<bean id="Log" class="com.aop.Log"/> <bean id="ReturnA" class="com.aop.ReturnA"/> <bean id="User_crud" class="com.service.User_crud"/> <aop:config> <aop:pointcut id="pointcut1" expression="execution( * com.service.User_crud.*(..))"/> <aop:advisor advice-ref="Log" pointcut-ref="pointcut1"/> <aop:advisor advice-ref="ReturnA" pointcut-ref="pointcut1"/> </aop:config>
其中aop:pointcut为切入点,expression值为切入点表达式,用于锁定需要附加日志的类的方法
格式为”返回值类型 方法的完整路径名(完整参数类型路径名) *代表任意字符 (..) 写于参数列表时表示任意参数
在aop:advisor标签中配置日志类,advice-ref为日志的bean的id,pointcut-ref为切入点id
所需要实现的功能api接口可以在下面这个包中便捷查找
整合mybatis和spring
需要在maven中导入
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.23</version> </dependency>
MyBatis-Spring 版本:
MyBatis-Spring MyBatis Spring Framework Spring Batch Java 2.0 3.5+ 5.0+ 4.0+ Java 8+ 1.3 3.4+ 3.2.2+ 2.1+ Java 6+
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和
SqlSession
并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的DataAccessException
。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个
SqlSessionFactory
和至少一个数据映射器类。在 MyBatis-Spring 中,可使用
SqlSessionFactoryBean
来创建SqlSessionFactory
。
在spring中配置数据源,这样可以省略mybatis配置文件中的数据源
<bean id="dateresource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value=""/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
SqlSessionFactoryBean创建
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:xml文件完整路径.xml" /> <property name="configuration" ref="classpath:mybatis配置文件完整路径.xml"/> </bean>
通过设置mapperLocations值在spring中注册mapper,可以使用*,如*/*/*/*.xml
通过设置configuration值在spring中导入mybatis配置文件
在创建了这个bean后mybatis中就不再需要MyBatis工具类了
创建
SqlSession的bean
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean>
要使用
SqlSession的bean,必须在mapper接口所在包下再添加一个实现了mapper的类
在类中添加如下代码
public class userDaoimp implements userDao{ private SqlSessionTemplate sqlSession; public userDaoimp(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public int getUSerSum() { userDao mapper = sqlSession.getMapper(userDao.class); return mapper.getUSerSum(); } }
之后要使用所实现的功能就直接在spring配置文件中注册userDaoimp类的bean就行
<bean id="userDaoimp" class="com.dao.userDaoimp"> <property name="sqlSession" ref="sqlSession"/> </bean>
然后使用这个bean就行
声明式事务
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个
DataSourceTransactionManager
对象:<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean>
利用tx命名空间进行管理事物
<!-- 配置事物管理--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 给那些方法配置事物--> <tx:attributes> <tx:method name="方法名"/><!-- 方法名可以为*--> <tx:method name=""/> <tx:method name=""/> </tx:attributes> </tx:advice> <!-- aop切入事物--> <aop:config> <aop:pointcut id="txPointcut" expression="* com.*(..)"/> <aop:advisor advice-ref="txAdvice"/> </aop:config>
使用tx:advice标签配置事物管理,利用aop:config标签切入事物