spring中文网:Spring 中文网 (springref.com)
Spring 使每个人可以更轻松、更安全地进行 Java 编程。对 Spring 的快速、简单性和关注成为世界上<<
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建
Spring是全面的和模块化的。Spring有分层的体系结构,这意味着你能选择使用它孤立的任何部分,它的架构仍然是内在稳定的。例如,你可能选择仅仅使用Spring来简单化JDBC的使用,或用来管理所有的业务对象
Spring Framework为任何类型的部署平台上的基于Java的现代企业应用程序提供了全面的编程和配置模型
WebTestClient
。IOC 是 Inversion of Control 的简写,译为**“控制反转”**,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
Spring 通过 IOC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IOC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
IOC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。
全程参与整个Java项目的进程,必须清楚了解资源创建的整个过程
不必关心资源创建过程的所有细节
开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可
这种行为也称为查找的被动形式
DI:Dependency Injection,翻译过来是依赖注入
依赖注入(Denpendency Injection,简写为 DI)是 Martin Fowler 在 2004 年在对“控制反转”进行解释时提出的。Martin Fowler 认为“控制反转”一词很晦涩,无法让人很直接的理解“到底是哪里反转了”,因此他建议使用“依赖注入”来代替“控制反转”
在面向对象中,对象和对象之间是存在一种叫做“依赖”的关系。简单来说,依赖关系就是在一个对象中需要用到另外一个对象,即对象中存在一个属性,该属性是另外一个类的对象
public class UserController {
@Autowired
private UserService userService;
}
IOC 思想基于 IOC 容器实现的,IOC 容器底层其实就是一个 Bean 工厂。Spring 框架为我们提供了两种不同类型 IOC 容器,它们分别是 BeanFactory 和 ApplicationContext
BeanFactory 是 IOC 容器的基本实现,也是 Spring 提供的最简单的 IOC 容器,它提供了 IOC 容器最基本的功能,由 org.springframework.beans.factory.BeanFactory 接口定义(用不到,内部人员使用)
ApplicationContext 是 BeanFactory 接口的子接口,是对 BeanFactory 的扩展。ApplicationContext 在 BeanFactory 的基础上增加了许多企业级的功能,例如 AOP(面向切面编程)、国际化、事务支持等
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.24version>
dependency>
不知道什么原因高版本下的spring-context不能用junit(已解决)
高版本的spring-context不能用junit-Java-CSDN问答
public class HelloWorld {
public void hello() {
System.out.println("hellospring");
}
命名为applicationContext.xml
@Test
public void testHello() {
//获取ioc容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
HelloWorld bean = (HelloWorld) ioc.getBean("hello");
bean.hello();
}
public class User {
private Integer id;
private String username;
private String password;
private Integer did;
private DUser duser;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String username, String password, Integer did) {
super();
this.username = username;
this.password = password;
this.did = did;
}
public User(String username, String password, Integer did, DUser duser) {
super();
this.username = username;
this.password = password;
this.did = did;
this.duser = duser;
}
public User(Integer id, String username, String password, Integer did, DUser duser) {
super();
this.id = id;
this.username = username;
this.password = password;
this.did = did;
this.duser = duser;
}
public int getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public DUser getDuser() {
return duser;
}
public void setDuser(DUser duser) {
this.duser = duser;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getDid() {
return did;
}
public void setDid(Integer did) {
this.did = did;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", did=" + did + ", duser=" + duser + "]";
}
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// HelloWorld bean = (HelloWorld) ioc.getBean("hello");
// bean.hello();
User bean = (User) ioc.getBean("user");
System.out.println(bean.toString());
在创建对象java类下一定要有无参构造,否则会报错。
当没有无参构造时:警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘user’ defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [pojo.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: pojo.User.< init>()
无参构造是固定的,而有参构造不是固定的。
User bean = (User) ioc.getBean("user");
User bean = ioc.getBean(User.class);
注意:根据类型获取bean时,要求IOC容器中有且仅有一个类型匹配的bean
若没有任何一个类型匹配的bean,此时抛出异常NoSuchBeanDefinitionException
若有多个类型匹配的bean,此时抛出异常NoUniqueBeanDefinitionException
<bean id="userOne" class="pojo.User">bean>
<bean id="userTwo" class="pojo.User">bean>
User bean = ioc.getBean("user",User.class);
实现前提是bean唯一
如果一个接口有多个实现类,这些实现类都配置了bean,可以根据接口类来获取bean吗?
不行,bean不唯一
结论:
根据类型来获取bean时,在满足bean的唯一性的前提下,其实只是看:【对象指定的类型】的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
相对的,通过bean的类型、bean所继承的类的类型、bean所实现的接口类的类型都可以获取到bean
property标签:通过组件类的setXX()方法给组件对象设置属性
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password" value="hai123">property>
bean>
java类的有参构造:
public User(String username, String password, Integer did) {
super();
this.username = username;
this.password = password;
this.did = did;
}
constructor-arg标签:这个标签必须与有参构造的属性个数对应,不然会报错
<bean id="userOne" class="pojo.User">
<constructor-arg value="hai">constructor-arg>
<constructor-arg value="h123">constructor-arg>
<constructor-arg value="1">constructor-arg>
bean>
如果用value来赋值,能不能得到null值?
不行,这样是在给一个变量赋值一个“null”的字面量,而不是一个空字节
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password" value="null">property>
bean>
应该用
标签
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password">
<null>null>
property>
bean>
如果password要赋值
解决方法:
xml实体 | 数学字符 | 符号意义 |
---|---|---|
< |
< | 小于 |
> |
> | 大于 |
& |
& | 和号 |
' |
’ | 单引号 |
" |
" | 引号 |
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password" value="<hai123>">property>
bean>
CDATA节其中的内容会原样解析
CDATA节是xml中一个特殊的标签,因此不能写在一个属性中
<property name="password" value="]]>">property>
eclipse有没有CDATA节的快捷键,求大佬们解答:
eclipse有没有CDATA节的快捷键-移动开发-CSDN问答
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password">
<value>]]>value>
property>
bean>
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password"><value>]]>value>property>
<property name="duser" ref="duserOne">property>
bean>
<bean id="duserOne" class="pojo.DUser">
<property name="did" value="1">property>
<property name="dname" value="A">property>
bean>
级联的方式,要保证提前为duser属性赋值或者实例化
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password"><value>]]>value>property>
<property name="duser" ref="duserOne">property>
<property name="duser.did" value="2">property>
<property name="duser.dname" value="B">property>
bean>
<bean id="duserOne" class="pojo.DUser">
<property name="did" value="1">property>
<property name="dname" value="A">property>
bean>
如果不先实例化,则报错为:Value of nested property ‘duser’ is null,它会先查找duser(如果值为空值),则后面的级联就不会执行了
<bean id="userOne" class="pojo.User">
<property name="id" value="1">property>
<property name="username" value="hai">property>
<property name="password"><value>]]>value>property>
<property name="duser">
<bean id="duserInner" class="pojo.DUser">
<property name="did" value="2">property>
<property name="dname" value="B">property>
bean>
property>
bean>
<bean id="duserOne" class="pojo.DUser">
<property name="did" value="1">property>
<property name="dname" value="A">property>
<property name="hobbys">
<set>
<value>movievalue>
<value>gamesvalue>
set>
property>
bean>
<bean id="duserOne" class="pojo.DUser">
<property name="did" value="1">property>
<property name="dname" value="A">property>
<property name="users">
<list>
<ref bean="userOne"/>
<ref bean="userTwo"/>
list>
property>
bean>
引用前提:beans有util的声明
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<bean id="duserOne" class="pojo.DUser">
<property name="did" value="1">property>
<property name="dname" value="A">property>
<property name="users" ref="userlist">property>
bean>
<util:list id="userlist">
<ref bean="userOne"/>
<ref bean="userTwo"/>
util:list>
beans>
spring为集合属性赋值{map,list,set,properties} - beibidewomen - 博客园 (cnblogs.com)
xmlns:p="http://www.springframework.org/schema/p"
<bean id="userTwo" class="pojo.User" p:id="3">bean>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.28version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.24version>
dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver">property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8">property>
<property name="username" value="root">property>
<property name="password" value="123456">property>
bean>
可以用properties文件来简化连接
<context:property-placeholder location="Sql.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${Sql.driver}">property>
<property name="url" value="${Sql.url}">property>
<property name="username" value="${Sql.username}">property>
<property name="password" value="${Sql.password}">property>
bean>
@SuppressWarnings("resource")
@Test
public void testContext() throws SQLException {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
DruidDataSource bean = classPathXmlApplicationContext.getBean(DruidDataSource.class);
System.out.println(bean.getConnection());
}
在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者
目前Spring Bean的作用域范围有五种:
bean标签下scope属性作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
WebApplicationContext环境下作用域 | 描述 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境 |
application | 限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境 |
如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。
Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:
<bean id="..." class="..." scope="singleton">
bean>
spring的bean的生命周期主要是创建bean的过程,一个bean的生命周期主要是4个步骤:
实例化,属性注入(依赖注入),初始化,销毁
但是对于一些复杂的bean的创建,spring会在bean的生命周期中开放很多的接口,可以让你加载bean的时候对bean做一些改变,因此spring的bean的生命周期总共有以下几步:
首先在spring中有一些特殊的bean会介入到其他bean的声明周期当中去,所以一个普通的bean的生命周期为:
在scope="singleton"
下生命周期:实例化、依赖注入、初始化是在加载IOC容器下就进行了
例:只执行
@SuppressWarnings("resource")
@Test
public void testContext() throws SQLException {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
}
时,就会加载生命周期中的实例化、依赖注入、初始化
在scope="prototype"
下生命周期:实例化、依赖注入、初始化是在创建加载对象下进行的,同时销毁 的周期不属于IOC容器管理所以不会执行
生命周期还受bean的后置处理器影响
完整的生命周期:
- 实例化
- 依赖注入
- 后置处理器BeanPostProcessor初始化之前处理
- 初始化
- 后置处理器BeanPostProcessor初始化之后处理
- 销毁
FactoryBean是Spring提供的一种整合第三方框架的常用机制,和普通的bean不同,配置一个FactoryBean类型的bean,在获取这个bean的时候得到的并不是class属性中配置的这个类的对象,而是Object()方法的返回值
public interface FactoryBean<T> {
/**
* The name of an attribute that can be
* {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
* factory beans can signal their object type when it can't be deduced from
* the factory bean class.
* @since 5.2
*/
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
*
If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
*
As of Spring 2.0, FactoryBeans are allowed to return {@code null}
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be {@code null})
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
@Nullable
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or {@code null} if not known in advance.
* This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
*
In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
*
This method can be called before this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
*
NOTE: Autowiring will simply ignore FactoryBeans that return
* {@code null} here. Therefore, it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or {@code null} if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
@Nullable
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* NOTE: If a FactoryBean indicates to hold a singleton object,
* the object returned from {@code getObject()} might get cached
* by the owning BeanFactory. Hence, do not return {@code true}
* unless the FactoryBean always exposes the same reference.
*
The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
*
NOTE: This method returning {@code false} does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* {@code isSingleton()} implementation returns {@code false}.
*
The default implementation returns {@code true}, since a
* {@code FactoryBean} typically manages a singleton instance.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
default boolean isSingleton() {
return true;
}
}
设置一个服务层service、依赖层dao、控制层controller
用控制层来控制服务层,服务层来调用依赖层中的方法去实现层层依赖
在xml文件中来配置bean
<bean class="controller.UserController">
<property name="userService" ref="userService">property>
bean>
<bean id="userDao" class="dao.UserDaoImpl">
bean>
<bean id="userService" class="service.UserServiceImpl">
<property name="userDao" ref="userDao">property>
bean>
手动装配
UserController bean = ioc.getBean(UserController.class);
自动装配
可以通过bean标签中的autowire属性自动装配的策略
自动装配策略:
no、default:表示不装配,即bean中的属性不会自动匹配某个bean为属性赋值,此时用默认值
byType:装配为bean中属性的类型赋值,需要在其它bean中找到其属性相关的类型bean
<bean id="userService" class="service.UserServiceImpl" autowire="byType">
<property name="userDao" ref="userDao">property>
bean>
注意:
a> 若通过类型没有找到如何一个类型匹配的bean,此时就不装配
b> 若通过类型找到了多个类型匹配的bean,此时会抛出异常
总结:当使用byType实现自动匹配,IoC中有且只有一个匹配的bean
byName:(通过类型找到了多个类型匹配的bean,但不是多个重复id名的bean)
和xml配置文件一样,注解本身并不能执行,具体功能是框架检测到注解标记的位置,然后针对这个位置按照标记注解的功能来执行具体操作
本质:所有的一切的操作都是java代码来完成的,xml和注解只是告诉框架中的java代码如何进行
spring为了知道程序员在那些地方什么注解(一般实体类不交给Ioc来管理),需要通过扫描的方法来进行操作
@Component:将类表示为普通文件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件
<context:component-scan base-package="controller,dao,service">context:component-scan>
注: 可以把所有包都放在一个包下,这样通过一个包来扫描所有注释:base-package=“spring”
一般情况下springMVC来扫描控制层,而spring来扫描其他层。
context:exclude-filter标签属性:type
<context:component-scan base-package="controller,dao,service">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
但是默认bean扫描一般是都扫描,所以要增加标签:use-default-filters=“false”,所设置的包下的类都不需要扫描
<context:component-scan base-package="controller,dao,service" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
默认值为类的小驼峰,即:类的首字母为小写的全类名
可以通过注释后加自定义id名(通过标识组件的注解的value属性来添加):
@Controller("controller")
@Autowired 能够标识的位置
@Autowired本质:
(1)默认通过byType的方式,在IoC容器中通过类型匹配某个bean为属性赋值
(2)如果有多个id相同的bean同注释下的类名字一样,注释会自动转换byName的方式来赋值
(3)若byType和byName都无法实现自动装配,即IoC容器中有多个类型匹配的bean,且这些bean的id和要赋值的属性的属性名都不一致,此时会抛出异常
(4)此时可以在要赋值的属性上加***@Qualifer***,通过该属性的value值,指定某个bean的id,将这个bean赋值
基于对接口的方法附蹭一些功能时,可以利用代理模式来实现不改变接口方法去增加方法的功能
代理模式本质:方法接口–>代理接口管理–>调用代理接口实现方法
代理类模板:
public class ProxyFactroy {
private Object target;
public ProxyFactroy(Object target) {
super();
this.target = target;
}
public Object getproxy() {
/**
* ClassLoader: 指加载动态代理类的类加载器
* Class>[]: 获取目标对象所以的class对象的数组
* InvocationHandler:设置代理类中的抽象方法怎么写
*/
ClassLoader classLoader = this.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// System.out.println();
// proxy代表代理对象,method标识要执行的方法,args标识要执行的方法用到的参数
Object result = method.invoke(target, args);
return result;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,h);
}
}
可以在代理类里增加一些功能,多用于日志功能。
AOP是一种设计思想,是软件设计领域中面向切面编程,它是面向对象编程的一种补充和完善,它以预编译方式和运行期间动态代理方式实现在不修改源代码的情况下给程序统一添加额外功能的一种技术(代理模式)。
(1)简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
(2)代码增强:把特定的功能封装到切面中,看哪里需要就往上套,被套用了切面逻辑的方法就被切面增强了。
AOP注意事项:
切面类和目标类都需要交给IOC容器管理
切面类必须通过@Aspect注解标识为一个切面
在spring的配置文件中配置aop:aspectj-autoproxy
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.1version>
dependency>
<context:component-scan base-package="annotation" >context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
package annotation;
public interface TestAspect {
int add(int i, int j);
}
package annotation;
import org.springframework.stereotype.Component;
@Component
public class TsetAspectImpl implements TestAspect {
@Override
public int add(int i,int j) {
int result = i + j;
System.out.println(result);
return result;
}
}
package annotation;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
*1.
*在切面中,需要通过指定的注解将方法标识为通知方法
* @Before:前置通知,在目标对象方法执行之前执行
* @After:后置通知,在目标对象方法执行之后执行
* @AfterReturning:返回通知,在目标对象方法执行返回之后执行
* @AfterThrowing:异常通知
* @Around:环绕通知
* 2.
* 第一个*表示任意的访问修饰符和返回值类型
* 第二个*表示类中任意的方法
* 。。表示任意的参数列表
* 类的地方也可以使用*,表示包下所以的类
* 3.
* 重用切入点表达式
* @Pointcut声明一个公共的切入点表达式
* @Pointcut("execution(* annotation.TsetAspectImpl.*(..))")
public void pointCut() {
}
* 4.
* 在通知方法的参数位置,设置JoinPoint类型的参数,就可以获取连接点所对应方法的信息
*
*
*/
@Component
@Aspect //将当前组件标志为切面
public class LoggerAspect {
@Pointcut("execution(* annotation.TsetAspectImpl.*(..))")
public void pointCut() {
}
//方法执行之前会执行
@Before("pointCut()")
// @Before(value = "execution(* annotation.TsetAspectImpl.*(..))")
// @Before("execution(public void dao.UserDaoImpl.saveUser())")
public void beforeadvicemethod(JoinPoint joinpoint) {
//获取连接点所对应方法的签名信息
Signature signature = joinpoint.getSignature();
//获取连接点所对应方法的参数
Object[] args = joinpoint.getArgs();
System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+Arrays.toString(args));
}
@After("pointCut()")
public void afteradvicemethod() {
System.out.println("后置通知");
}
/**
* 在返回通知中若要获取目标对象方法的返回值
* 只需要通过@AfterReturning中的returning属性
* 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void AfterReturningmethod(JoinPoint joinPoint,Object result) {
Signature signature = joinPoint.getSignature();
System.out.println("方法:"+signature.getName()+"结果:"+result);
}
/**
* 在异常通知中若要获取目标对象方法的异常
* 只需要通过@AfterThrowing中的throwing属性
* 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
*/
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void AfterThrowingmethod(Throwable ex) {
System.out.println("异常:"+ex);
}
@Around(value = "pointCut()")
public Object aroundMethod(ProceedingJoinPoint JoinPoint) {
Object proceed = null;
Signature signature = JoinPoint.getSignature();
Object[] args = JoinPoint.getArgs();
try {
//前置通知
System.out.println("LoggerAspect,方法:"+signature.getName()+",参数:"+Arrays.toString(args));
//表示目标对象方法的执行
proceed = JoinPoint.proceed();
//返回通知
System.out.println("方法:"+signature.getName()+"结果:"+proceed);
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
//异常通知
System.out.println("异常:"+e);
}finally {
//后置通知
System.out.println("后置通知");
}
return proceed;
}
}
用注释的@Order(value)通过value属性来设置优先级,默认值Interger的最大值
value值越小,优先级越高
public @interface Order {
/**
* Default order value for elements not explicitly annotated with {@code @Order},
* equal to the value of {@code Integer.MAX_VALUE / 2}.
*
* @since 5.6
* @see Order#value
*/
@API(status = EXPERIMENTAL, since = "5.6")
int DEFAULT = Integer.MAX_VALUE / 2;
/**
* The order value for the annotated element (i.e., field, method, or class).
*
* Elements are ordered based on priority where a lower value has greater
* priority than a higher value. For example, {@link Integer#MAX_VALUE} has
* the lowest priority.
*
* @see #DEFAULT
*/
int value();
<context:component-scan base-package="annotation" >context:component-scan>
<aop:config>
<aop:pointcut expression="pointCut" id="execution(* annotation.TsetAspectImpl.*(..))"/>
<aop:aspect ref="loggerAspect">
<aop:before method="beforeAdviceMethod" pointcut-ref="pointCut"/>
<aop:after method="afterAdviceMethod" pointcut-ref="pointCut"/>
<aop:after-returning method="afterReturningAdviceMethod" returning="result" pointcut-ref="pointCut"/>
<aop:after-throwing method="afterThrowingAdviceMethod" throwing="ex" pointcut-ref="pointCut"/>
<aop:around method="aroundAdviceMethod" pointcut-ref="pointCut"/>
aop:aspect>
aop:config>
Spring 框架对JDBC进行封装,使用JdbcTemplate 方便实现对数据库操作
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.3.10version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.28version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.24version>
dependency>
可能遇到的问题:关于不能用SpringJUnit4ClassRunner.class
Spring Junit测试找不到SpringJUnit4ClassRunner.class_TianXinCoord的博客-CSDN博客
<context:property-placeholder location="classpath:Sql.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${Sql.driver}">property>
<property name="url" value="${Sql.url}">property>
<property name="username" value="${Sql.username}">property>
<property name="password" value="${Sql.password}">property>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
package spring;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pojo.DUser;
//指定当前测试类在spring的测试环境中执行,此时就可以通过注入的方式直接获取IOC容器中的bean
@RunWith(SpringJUnit4ClassRunner.class)
//设置spring测试环境的配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class TestJdbcTemplate {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
public void testadd() {
String sql = "insert into d_user values(null,?)";
//增删改都用update
jdbcTemplate.update(sql,"D");
System.out.println("add");
}
@Test
public void testqueryById() {
String sql = "select * from d_user where d_id = ?";
DUser dUser = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(DUser.class),1);
System.out.println(dUser.toString());
}
@Test
public void testquery() {
String sql = "select * from d_user";
List<DUser> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(DUser.class));
System.out.println(list);
}
@Test
public void testgetCount() {
String sql = "select count(*) from d_user";
Integer integer = jdbcTemplate.queryForObject(sql,Integer.class);
System.out.println(integer);
}
}
事务功能的相关操作全部通过自己编写代码来实现:
public Connection getConnection() {
Connection connection = null;
try {
connection.setAutoCommit(false);
connection.commit();
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
编程式的实现方式存在缺陷:
既然事务控制的代码有迹可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。
封装起来后,我们只需要在配置文件中进行简单的配置即可完成操作。
所以总结两种概念事务:
<context:property-placeholder location="classpath:Sql.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${Sql.driver}">property>
<property name="url" value="${Sql.url}">property>
<property name="username" value="${Sql.username}">property>
<property name="password" value="${Sql.password}">property>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
在需要被事务管理的方法上,添加**@Transactional**注解,该方法就会被事务管理
@Transactional注解标识的位置:
@Transactional 注解参数详解,以及注解的使用特性说明(典藏版)_transactional注解参数_Java Punk的博客-CSDN博客
事务隔离级别:
与持久层有关,数据库系统必须具有隔离并发运行各个事务的能力。一个事务与其它事务隔离的程度称为隔离级别。SQL标准中规定了多个事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
事务 B 去查询了事务 A 修改过的数据,但是此时事务 A 还没提交,所以事务 A 随时会回滚导致事务 B 再次查询就读不到刚才事务 A 修改的数据了,查询到的数据没有意义,这种叫做脏读
事务A去第一次查询一条数据,同时事务B可以修改这条数据并提交事务(避免脏读),之后执行事务B,事务A再去查询这条数据时就查询不到原先的数据,而是事务B修改过的数据,这样多次查询到的数据是不一样的,这种数据叫做不可重复读
事务A第一次查询一条数据,之后事务B并发执行在这条数据上进行了修改同时提交了事务,当事务A再次查询这条数据时发现数据改变了,简单来说,就是一个事务用一样的 SQL 多次查询,结果每次查询都会发现查到一些之前没看到过的数据,这种就叫幻读
<context:property-placeholder location="classpath:Sql.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${Sql.driver}">property>
<property name="url" value="${Sql.url}">property>
<property name="username" value="${Sql.username}">property>
<property name="password" value="${Sql.password}">property>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="testXML"/>
<tx:method name="*"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="tx" pointcut="execution(* service.TsetServiceImpl.*(..))"/>
aop:config>
注: 基于xml实现的声明式事务,必须引入aspectj的依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.1version>
dependency>