Spring
- 简介
- 优点
- 七大模块
- 框架学习流程:
简介
Spring之父——Rod Johnson
Spring是一个轻量级Java开发框架,最早由Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(全栈-一站式)轻量级开源框架,为开发Java应用程序提供全面的基础框架支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
2002,首次推出Spring的框架雏形:interface21框架
2004.3.24 Spring以interface21框架为基础重新设计发布Spring1.0版本
优点
Spring是一个免费开源框架
Spring是一个轻量级、非入侵式框架
控制反转(IOC)面向切面编程(AOP)
支持事务处理,对框架整合支持
七大模块
框架学习流程:
本质:反射创建对象
Spring(是一个IOC和AOP容器):
- 导入jar
导入相关jar:四个核心框架包(beans、context、core、expression)+一个日志包 (common-logging) - 编写配置文件;
spring-config.xml - 测试
junt4测试
IOC容器
ioc: 反向资源访问
即容器主动将资源推送给管理的组件,组件只需要选择一种合适的方式来接受资源 ,将bean的创建交给第三方
BeanFactory与ApplicationContext
Spring ioc在创建的时候,通过配置文件去自动创建对象和赋值
// 加载xml后ApplicationContext 去初始化配置的Bean,通过构造方法。反射机制去创建。
ApplicationContext ioc=new ClassPathXmlApplicationContext(“spring-config.xml”);
spring ioc 容器在读读取bean配置之前 容器进行实例化
实例化方式一:BeanFactory(低层)
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader= new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("applicationContext.xml");
Emp emp1 = (Emp) beanFactory.getBean("emp");
实例化方式二:ApplicationContext (几乎所有的场合都是用applictionContext,其为beanFactory的子接口)
applicationContext 其实现类:
- ClassPathXmlApplicationContext 从类路径下加载配置文件
- FileSystemXmlAppplicationContext 从文件系统中加载配置文件
<beans>
<bean id="emp" class="com.Emp">
<property name="empno" value="1"></property>
</bean>
</beans>
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp = (Emp) applicationContext.getBean("emp");
关系
- BeanFactory是Spring的早期接口,称为Spring的Bean工厂,ApplicationContext是后期更高级接口,称之为Spring 容器;
- ApplicationContext在BeanFactory基础上对功能进行了扩展,例如: 监听功能、国际化功能等。BeanFactory的API更偏向底层,ApplicationContext的API大多数是对这些底层API的封装;
- Bean创建的主要逻辑和功能都被封装在BeanFactory中,ApplicationContext不仅继承了BeanFactory,而目ApplicationContext内部还维护着BeanFactory的引用,所以,ApplicationContext与BeanFactorv既有继承关系,又有融合关系。
- Bean的初始化时机不同,原始BeanFactory是在首次调用getBean时才进行Bean的创建,而ApplicationContext则是配置文件加载,容器一创建就将Bean都实例化并初始化好。
获取bean
ioc 配置bean:其都单例的
通过组件名字去查找
Person person = (Person) applictionContext.getBean("person");
通过类型去查找
(此类型的bean有多个,报错)
Person person = (Person) applictionContext.getBean(Person.class);
同时名称、类型查找
Person person = (Person) applictionContext.getBean("person02",Person.class);
bean 属性
id:类名字
当bean无id时,容器默认以其全类名为id
<beans>
<bean class="com.Emp">
<property name="empno" value="1"></property>
</bean>
</beans>
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp = (Emp) applicationContext.getBean("com.Emp");
name:别名
当有多个别名时,可用“,”分隔。
实例化bean两种方式之构造方法
set方法(property )
<property name="name" value="xusx"></property>
构造方法(constructor-arg)
index:构造方法参数索引位置
type:数据类型
name:属性名
ref:引用
注:当其构造方法为多个时(方法重载,参数的个数和类型),type=数据类型
<constructor-arg value="xusx" index="0" ></constructor-arg>
<constructor-arg value="11" index="1" ></constructor-arg>
value 包含特殊字符:![cdata][]
<constructor-arg index="1">
<value><![CDATA[属性值]]></value>
</constructor-arg>
bean 之间的引用关系(引用类型)
外部引入
<property name="dept" ref="bean的id"></property>
内部引入
<property name="dept" >
<bean class=''></bean>
</property>
数组
<property name="books">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>
级联-list
<property name="carList">
<list>------new list
<bean class="com.yinhai.bean.Car" >
<property name="name" value="car1"></property>
</bean>
<bean class="com.yinhai.bean.Car" >
<property name="name" value="car2"></property>
</bean>
<ref bean="car"></ref>
<ref bean="car"></ref>
</list>
</property>
级联-map
<map> ------new map
<entry key="car001" value-ref="car"></entry>
<entry key="001" value="001value"></entry>
<entry key="myCar">
<bean class="com.yinhai.bean.Car">
<property name="name" value="myCar"></property>
</bean>
</entry>
</map>
parent:继承,属性的复用
需要改变的属性 property (本质为set方法)去处理
<bean id="car" class="com.yinhai.bean.Car">
<property name="name" value="bmw"></property>
<property name="color" value="red"></property>
</bean>
<bean id="car2" class="com.yinhai.bean.Car" parent="car">
<property name="color" value="black"></property>
</bean>
abstract=true
则此bean为抽象的不会被实例化。只可被用来继承。此bean就可作为一个模板
depends-on=‘id’
设定bean前置依赖的Bean,前置依赖的Bean会在本Bean创建之前实例化,依赖多个Bean 可逗号隔开
bean的创建顺序默认是按配置顺序
改变创建顺序
init-method
初始化方法(容器在实例化bean时,bean 执行的方法)
zy-init:true\false
容器在加载时就去实例化bean false:在获取bean时容器才会去实例化-------懒加载
bean的作用域
- socope=“singleton” 默认为单例,在容器初始化的时候创立
- scope=“prototype” 多实例 每次请求时创建新的
- scope=“request” 同一次请求创建一个实例
- scope=“session” 同一次会话创建一个实例
autowire自动装配
- byName
- byType :当有多个类型时会报错
如果被注入的属性是bean类型时,那么可以使用此标签属性自动设置。
本质: 是通过set方法,经过测试发现set开头的方法名即可。为规范是还是按照正常的get\set
public void setDept(Dept dept) {
this.dept = dept;
}
<bean id="emp" class="com.Emp" autowire="byName">
</bean>
<bean id="dept" class="com.Dept">
<constructor-arg name="deptNo" value="2222"></constructor-arg>
</bean>
实例化bean两种方式之工厂模式
工厂模式创建bean
静态工厂:工厂不身不需要创建对象,通过静态方法调用**
factory-method:创建方法
public static Person createPerson(String name){
Person person=new Person();
person.setName(name);
person.setAge("11");
return person;
}
<!-- class为静态工厂类的全限定类名 factory-method为使用的静态方法
<bean id="personStaticFac" class="com.yinhai.bean.PersonFactory" factory-method="creatPerson">
<constructor-arg name="name" value="xusxxxxxxxxxx"></constructor-arg>
</bean>
Person p = (Person) ioc.getBean("personStaticFac");
实例工厂:工厂本身需要创建对象
public Person creatPerson(String name){
Person person=new Person();
person.setName(name);
person.setAge("18");
return person;
}
<bean id="personFac" class="com.yinhai.bean.PersonFactory" >
</bean>
<bean id="personInstance" factory-bean="personFac" factory-method="creatPerson">
<constructor-arg name="name" value="aaa"></constructor-arg>
</bean>
Person p = (Person) ioc.getBean("personInstance");
InitializingBean初始化接口
作用范围:实现此接口的bean
@Override
public void afterPropertiesSet() throws Exception {
}
自定义标签
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>形式的标签,称之为自定义标签,自定义标签的解析流程也是 Spring xml扩展点方式之一
切换环境
除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境
<beans profile="test">
......
</beans>
<beans profile="dev">
......
</beans>
设置别名
<bean id="empppp" class="com.Emp" autowire="byType" >
<property name="empno" value="111111111"></property>
</bean>
<alias name="empppp" alias="test"></alias>
指定激活环境:
- 使用命令行动态参数,虚拟机参数位置加载-Dspring.profiles.active=test
- 使用代码的方式设置环境变量 System.setProperty(“spring.profiles.active”,"test”)
实例化bean两种方式之反射
见反射链接
bean生命周期
- Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否不是延迟加载的,是否不是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化
- Bean的初始化阶段:Bean的创建之后还仅仅是个“半成品”,好需要对Bean实例的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitiallizingBean接口的初始化方法、执行自定义初始化init方法等
- Bean的完成阶段:经过初始化阶段,Bean就成为一个完整的Spring Bean,被存储到单例池singleonObjects中,即完成了Spring Bean的整个生命周期
Bean的循环依赖(初始化阶段)
三级缓存机制
Aware接口
Aware接口是一种框架辅助属性注入的一种思想,其框架中也可以看到类似的接口,框架具备高度封装性,我们接触到的一般都是业务代码,一个底层的API不能轻易的获取到,但是这不意味着永远用不到这些对象,如果用到了,就可以使用框架提供的类似Aware的接口,让框架给我们注入对象。
Bean 后置处理器接口之BeanPostProcessor
BeanPostProcessor
作用范围:所有bean
类似于过滤器,操作空间大
Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程
- 后置BeanPostProcessor接口
触发条件:
public Object postProcessBeforeInitialization(Object bean, String beanName)--------->init-method方法 之前
public Object postProcessAfterInitialization(Object bean, String beanName) --------->init-method方法 之后
配置实现接口的bean
<bean id="emp" class="com.Emp" autowire="byType" init-method="init">
<property name="empno" value="1"></property>
</bean>
导入外部属性文件
Spring2.0:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
<property name="locations" value="classpath:jdbc.properties">
</property>
</bean>
Spring 2.5
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
外部加载配置文件:classpath(固定写法)--加载类路径下的资源
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
spEl表达式 #{}
- 字面值 value=#{‘啊’}
- 引用静态属性 value=#{T(java.lang.math).PI}
- 引用bean value=#{bean的id}
- 引用bean属性 value=#{bean.属性}
- 运算符
注解
本质:使用注解去代替xml的配置
通过注解完成对象的创建、属性的赋值。需要依赖spring-context,在加入时候会间接的加入依赖spring-aop
注解原理
注解快速配置Bean
(通过注解加入到ioc容器中管理)
导入注解包:spring-aop
特定组件:
- @component:基本注解
- @Respository:标识 持久层组件
- @Service :标识 服务层组件
- @Controller: 标识 控制层组件
对于扫描到的组件,Spring 有默认的命名策略。使用非限定类名,第一个字母小写; 也可以在注解中通过Value属性值标识组件的名称 ------如:@component(“asdas”)
- @component 替代 beanXML配置
@Scope 作用域
@Scope(value = “singleton\prototype”)------不配置默认单例
扫描包 component-scan
只有添加扫描包后,其下被配置的扫描组件才会起作用
<!-- 配置自定掃描包 -->
<context:component-scan
base-package="com.yinhai"
resource-pattern="baen/*.class" // 只匹配此路径下的类1
>
<context:component-scan base-package="com.yinhai">
<context:exclude-filter type="" expression=""></context:exclude-filter>
<context:include-filter type="" expression=""></context:include-filter>
</context:component-scan>
type:annotation–类型为注解 | assignable–类名
expression:全类名
exclude:排除不要的
include:只扫描要的
使用
若使用include-filter去定制扫描内容,要在use-default-filters="false"的情况下,不然会“失效”,被默认的过滤机制所覆盖
context:exclude-filter与context:include-filter 相反,用来告知哪些类不需要注册成Spring Bean,同样注意的是:在use-default-filters="false"的情况下,exclude-filter是针对include-filter里的内容进行排除。
关闭默认全部扫描<context:component-scan base-package=“com.yinhai” use-default-filters=“false”>
自动装配Bean
开启注解: <context:annotation-config/>
@Autowired
场景:属性或方法参数
根据Type查找
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
@Autowired(required = false)
false时,当找不到则给null ,
true时,找不到则报错
通过 byType
<bean id="cat" class="bean.Cat">
<property name="name" value="zz"></property>
</bean>
<bean id="dog" class="bean.Dog">
<property name="name" value="xx"></property>
</bean>
<bean id="person" class="bean.Person" ></bean>
public class Person {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public Person(){}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
}
@Qualifier(id) ----显式指定bean id
场景:属性或方法参数,通常与@Autowired 组合使用
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
指定具体id匹配
可组合使用
@Autowired
@Qualifier(“personServiceChilden”)
PersonService personService;
@Resource
属性或方法参数
先根据Type查找,当前存在多个类型时,则根据Name去查找
@Autowired与@Resource区别
- 来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
- 依赖查找的顺序不同:@Autowired 默认按照类型进行匹配,如果容器中有多个相同类型的Bean,配合使用@Qualifier指定具体Bean装配,而 @Resource 先根据名称再根据类型查询;
- 支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
- 依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
- 编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误
@Value(“xxx”)
场景:属性或方法参数
- 直接赋值字符串–意义不大
@Value("xusx")
private String name;
- 从已加载的配置文件中获取
@Value("${name}")
private String name;
- 参数赋值
public User UserFactory(@Value("${jdbc.url}") String url){。。。}
非自定义bean注解
@Bean
非自定义Bean不能像自定义Bean那样通过@Component进行管理,非自定义Bean要通过工厂的方式实例化。使用@Bena注解。@Bean可指定BeanName ,若不指定就是当前工厂方法名,
当Bean工厂需要注入参数时可使用:
- 使用@Autowired根据类型进行Bean的匹配,@Autowired可以忽略不写
- 使用@Qualifier根据名称进行匹配
- @Value 数据赋值 :在参数中赋值
@Component
public class UserFactory {
@Bean
public User userFactory(@Value("${jdbc.url}") String url,
EmpService empService){
List<Emp> all = empService.findAll();
System.out.println(all);
System.out.println(url);
User user = new User();
user.setName("test-factory");
user.setAge("1");
return user;
}
}
配置类(取代xml配置)
@Configuration:注解标注为配置类
- @ComponentScan:扫描包 -@ComponentScan(“com.yinhai”)
<context:component-scan base-package="com.yinhai"></context:component-scan>
- @PropertySource:加载配置文件 -@PropertySource(“classpath:jdbc.properties”)
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
- @Import: 加载其他配置类 -@Import(DataSourceConfig.class)
<import resource="spring-dataSource.xml"></import>
@Configuration
@ComponentScan("com.yinhai.xusx")
@Import(DataSourceConfig.class)
public class SpringConfiguration {
}
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.yinhai.xusx.mapper")
public class DataSourceConfig {
@Bean("dataSource")
public DataSource dataSource(
@Value("${jdbc.driverClassName}") String driverClassName,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password
) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
注: 配置类下mapper和其mapper.xml必须为相同的路径下,会默认按照mapper路径去查找mapper.xml,否则抛出此异常,若为配置文件则是指定mapper.xml的路径
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yinhai.xusx.mapper"></property>
</bean>
AnnotationConfigApplicationContext加载配类
ClassPathXmlApplicationContext加载配置文件
ApplicationContext applicationContext= new AnnotationConfigApplicationContext(SpringConfiguration.class);
其他配置注解
@Profile:用与类或方法上,标注其属于哪个环境。只有激活当前环境,才会加入spring容器中。不标注环境则任何环境都可以加入spring
@Profile("test)
可以使用以下两种方式指定激活环境:
- 命令行动态参数,虚拟机参数位置加载-Dspring.profiles.active=test
- 代码的方式设置环境变量 System.setProperty(“spring.profiles.active”,"test”);
System.setProperty("spring.profiles.active","test");
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) applicationContext.getBean("person");
@Component("person")
@Profile("test")
public class Person {}
自动装载属性(全自动属性赋值)------XML方式
对象:引用类型属性
目的:省去重复的property赋值,其自动匹配。
autowire=“byName” :属性名与存在的bean的id做匹配
<bean id="student" class="com.yinhai.Student" autowire="byName">
<property name="name" value="aaa"></property>
<property name="age" value="1"></property>
</bean>
<bean id="school" class="com.yinhai.School">
<property name="schoolName" value="打大学"></property>
</bean>
private String name;
private int age;
private School school;
打印:
shcool 被自动赋值
Student{name='aaa', age=1, school=School{schoolName='打大学'}}
autowire=“byType” : 属性名类型与存在bean的类型做匹配
在ByType中,声明的bean类型只能全局唯一,多余会报错。
autowire=“constructor” : 属性名先于类型匹配。其次在与id匹配
autowire=“default” : 赋值null
导入其他配置文件
<import resource="classpath:beans.xml"></import>
classpath 和 classpath* 区别
classpath:只会到你的class路径中查找找文件;
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找.
注解读取配置文件内容
格式:@Value(“${}”)
<context:property-placeholder location="classpath:test.properties"></context:property-placeholder>
@Value("${name}")
private String name;
注:主的配置文件不能包含在通配符的范围内,否则会造成死循环。
javaConfig实现配置
@Configuration类似于 beans
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
AOP切面编程
概念
是指在程序运行期间,将某段代码动态的切入指定方法的指定位置进行的那种编程方式
思想方案(底层动态代理)
动态代理技术,在运行期间对目标对象的方法经行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法。
- 在目标类源代码不变的情况下,增加功能
- 减少代码重复
- 专注业务逻辑代码
- 解耦合,将日志、事务等于业务逻辑代码分开
动态代理两种方式:
- 基于接口-jdk动态代理 Spring默认为此模式
- 基于类–Cglib
JDK动态代理:基于接口
public interface UserService {
public void showData();
}
public class UserServiceImpl implements UserService {
@Override
public void showData() {
System.out.println("showData.............");
}
}
public class MyAdvice {
public void add(){
System.out.println("增强···");
}
}
public class MyBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
public ApplicationContext applicationContext;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("userService".equals(beanName)){
Object o = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MyAdvice myAdvice = (MyAdvice) applicationContext.getBean("myAdvice");
myAdvice.add();
final Object invoke = method.invoke(bean, args);
return invoke;
}
});
return o;
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
效果
Cglib动态代理:基于类
public class PersonAop {
public void read(){
System.out.println("read--------");
}
}
public class PersonAdvice {
public void beforeRead(){
System.out.println("beforeRead");
}
}
// 目标对象
PersonAop personAop = new PersonAop();
// 通知对象
PersonAdvice personAdvice = new PersonAdvice();
// 增强器对象
Enhancer enhancer = new Enhancer();
// 增强器设置父类
enhancer.setSuperclass(personAop.getClass());
// 增强其回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
personAdvice.beforeRead();
Object invoke = method.invoke(personAop, objects);
return invoke;
}
});
// 创建代理对象
PersonAop person1= (PersonAop) enhancer.create();
// 测试
person1.read();
注:静态代理
相关概念
- 目标对象(Target) 被增强发放所在的对象
- 代理对象(Proxy) 对目标对象进行增强后的对象,客户端实际调用的对象。
- 连接点(Joinpoint) 目标对象中可以被增强的方法
- 切入点(Pointcut) 目标对象中实际被增强的方法
- 通知\增强(Advice) 增强部分的代码逻辑
- 切面(Aspect) 增强和切入点的组合
- 织入(Weaving) 将通知和切入点动态组合的过程
Spring AOP:对动态代理规范,但其比较笨重
AspectJ:一个专业的AOP的开源框架。Spring框架已经集成了aspect,通过Spring就可以使用。
实现方式
XML配置
- 准备目标类、 准备通知类交给Spring管理
- 配置切点表达式(哪些方法被增强)
- 配置织入(切点被哪些方法增强,是前后还是后置)
<!--目标类-->
<bean id="userService" class="com.yinhai.xusx.aop.impl.UserServiceImpl"></bean>
<!--通知类-->
<bean id="myAdvice" class="com.yinhai.xusx.aop.MyAdvice"></bean>
<aop:config>
<!--切点配置:哪些方法被增强-->
<aop:pointcut id="myponitcut" expression="execution(* com.yinhai.xusx.aop.impl.UserServiceImpl.showData())"/>
<!--织入配置:切点与通知的结合-->
<aop:aspect ref="myAdvice">
<aop:before method="add" pointcut-ref="myponitcut"></aop:before>
</aop:aspect>
</aop:config>
切点表达式的配置方式
- pointcut-ref 引入切点表达式
<aop:before method="add" pointcut-ref="myponitcut"></aop:before>
- pointcut 直接写入表达式
<aop:before method="add" pointcut="execution(* com.yinhai.xusx.aop.impl.UserServiceImpl.showData())"></aop:before>
切点表达式语法
execution([访问修饰符] 返回值类型 包名.类名.方法名(参数列表))
- 访问修饰符可以忽略不写
- 返回值类型、包名、类名、方法名 可以用 * 来表示任意
- 包名与类名之间使用单点(.)表示该包下的类;使用双点(..)表示该包及其子包下的类
- 参数列表双点(..) 表示任意参数
xml的两种配置方式
- <aspect>
在配置文件中配置通知方式 - <advisor>
配置实现不同的通知接口的通知类 (MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice、MethodInterceptor)
public class MyAdviceType implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知");
}
}
<bean id="myAdviceType" class="com.yinhai.xusx.aop.MyAdviceType"></bean>
<aop:config>
<!--切点配置:哪些方法被增强-->
<aop:pointcut id="myponitcut" expression="execution(* com.yinhai.xusx.aop.impl.UserServiceImpl.showData())"/>
<!--配置实现通知方式接口的通知类-->
<aop:advisor advice-ref="myAdviceType" pointcut-ref="myponitcut"></aop:advisor>
</aop:config>
五种通知方式
配置:<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式”></aop:通知类型>
- aop:before 前置通知。指定增强的方法在切入点方法之前执行
- aop:after-returning 后置通知。 指定增强的方法在切入点之后执行
- aop:after 最终通知。无论怎样都执行
- aop:around 环绕通知。指定增强的方法在前后都执行
- ProceedingJoinPoint 连接点,proceed()执行目标方法
<aop:around method="aroundAdvice" pointcut-ref="myponitcut"/>
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前");
joinPoint.proceed();
System.out.println("环绕后");
}
- aop:throwing 异常抛出通知。指定增强的方法出现异常
- 通知类 入参 Throwable 参数名称在xml配置
public void errorNotice(Throwable e){
System.out.println("异常通知");
}
配置
<aop:aspect ref="myAdvice">
<aop:after-throwing method="errorNotice" pointcut-ref="myponitcut" throwing="e"/>
</aop:aspect>
aop:aspect与aop:advisor区别:
- 语法形式不同
- aspect是通过配置来确认通知类型,更加灵活
- advisor是通过实现接口来确认通知类型
- 可配置的切面数量不同
- 一个advisor只能配置一个固定通知和一个切点表达式
- 一个aspect可以配置多个通知和多个切点表达式任意组合
- 使用场景不同
- 允许随意搭配情况下可以使用aspect配置
- 如果通知类型单一、切面单一的情况下可以使用advisor进行配置
- 在通知类型已经固定,不用认为指定通知类型时,可以使用advisor进行配置,例如spring的事务控制配置
注解配置
- @aspect | @component|注册bean
- 导入相关jar
- 配置文件开启aop注解:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
- @EnableAspectJAutoProxy 注解开启aop注解
五种通知方式
- @Before 前置通知,目标方法执行前执行
- @After 后置通知,目标方法执行后执行,不管是否有异常
- @AfterRunning 目标方法返回结果之后执行,有异常不执行
- @AfterThrowing 目标方法抛出异常后执行
- @Around 环绕通知,围绕着方法执行,有异常不执行
通知方法执行顺序:
正常:Before After AfterRunning
异常:Before After AfterThrowing
@Aspect
@Component
public class LogUtils {
// 织入配置:切点与通知的结合
@Before("execution(* service.UserServiceImpl.*(..))")
public void beforeMethod(){
System.out.println("前置通知");
}
@Before("execution(public void com.yinhai.service.impl.PersonServiceImpl.test(String))")
public static void test(JoinPoint joinPoint){
// 获取参数
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.asList(args).toString());
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println(name);
System.out.println("1111111111111111111111111111");
}
Pointcut :重用----抽取了要切入的方法
@After("LogUtils.all()")
public static void test2(JoinPoint joinPoint){
System.out.println("222222222222222");
}
@Pointcut("execution(public void com.yinhai.service.impl.PersonServiceImpl.test(String))")
public static void all(){
// 随便被声明的方法,无实际作用
}
Pointcut+空方法:目的只是为了抽取表达式(空方法为载体),等同于 XML配置的pointcut-ref=“切点表达式id”;为解决复用、维护;使用类名.方法
方法上直接表达式:等同于XML配置的pointcut=“表达式”
AOP使用场景:日志、权限控制、安全控制、是服务控制
静态代理:房屋主人–出租房屋–房屋中介
/**
* 业务接口
*/
public interface Rent {
void rent();
}
------------------------
/**
* 业务类实现业务接口
*/
public class Host implements Rent{
public void rent() {
System.out.println("出租房租");
}
}
------------------------
/**
* 代理
* 实现其业务接口
* 组合-设置要代理的对象,
* 可自由扩展
*/
public class HostProxy implements Rent {
private Host host;
public void setHost(Host host) {
this.host = host;
}
public void rent() {
printLog();
host.rent();
}
public void printLog(){
System.out.println("扩展日志");
}
}
-----------------------
测试
Host host=new Host();
HostProxy hostProxy=new HostProxy();
hostProxy.setHost(host);
hostProxy.rent();
注:实现接口+组合业务类
场景:尤其是公司的祖传代码,修改功能时不改变原有的基础上进行代理将很完美解决
事务
事务(Transaction)是访问并可能更新数据库中各项数据项的一个程序执行单元(unit)。 事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。事务是逻辑上的一组操作,要么全部成功,要么全部失败。
事务的四个特征(ACID)
1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
2. 一致性(Consistency
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。
也就是说事务前后数据的完整性必须保持一致。
3. 隔离性(Isolation)
隔离性是指一个事务的执行不能有其他事务的干扰,事务的内部操作和使用数据对其他的并发事务是隔离的,互不干扰。
4. 持久性(Durability)
持久性是指一个事务一旦提交,对数据库中数据的改变就是永久性的。此时即使数据库发生故障,修改的数据也不会丢失。接下来其他的操作不会对已经提交了的事务产生影响。
Spring对事务的管理分为编程式事务和声明式事务管理的两种方式
什么是脏读、不可重复读、幻读
脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
比如在事务 A 修改数据之后提交数据之前,这时另一个事务 B 来读取数据,如果不加控制,事务 B 读取到 A 修改过数据,之后 A 又对数据做了修改再提交,则 B 读到的数据是脏数据,此过程称为脏读。
不可重复读: 不可重复读是指在数据库访问中,一个事务范围内多次查询却返回了不同的数据值。这是由于在查询间隔中,其他事务修改并提交而引起的
幻读: 幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
事务的隔离级别
解决事务并发存在问题:脏读、不可重复读、幻读
isloation属性:指定事务的隔离级别。
事务的传播行为
解决事务嵌套问题
编程式事务(了解即可)
编程式事务指的是通过编码方式实现事务,编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务
声明式事务基于AOP,将具体业务逻辑与事务处理解耦,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者发生异常时回滚事务。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。
声明式事务有两种方式:
- 基于 @Transactional 注解的方式。
- 基于配置文件(xml)中配置相关的事务规则声明,
XML方式配置
- 配置事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
- 事务通知
name:切点方法名称
isolation:事务的隔离级别
propogation:事务的传播行为
timeout:超时时间 -1 永久
read-only:是否在只读
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="get*"read-Only="true"/>----通配符:get开头的 ----设置相关属性
</tx:attributes>
</tx:advice>
- 配置事务切面
<aop:config>
<aop:pointcut expression="execution(* com.yinhai.service.impl.PersonService.*(..))" id="txPointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
注解方式配置
<tx:annotation-driven 默认配置的事务管理器名称为:transactionManager,当DataSourceTransactionManager名字不为transactionManager则显示的指定transaction-manager=“XXX”
<tx:annotation-driven transaction-manager="transactionManageXXXXr"></tx:annotation-driven>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
@EnableTransactionManagement 代替
<tx:annotation-driven transaction-manager="transactionManageXXXX"></tx:annotation-driven>
@Bean 才采用非自定义Bean 完成transactionManager的IOC注册
@Transactional(属性配置)
适应在类或者方法上:作用域不同
运行时异常都会回滚,编译时异常不会回滚。
属性:rollbackFor{类.class,类.class}----------让不需要回滚的回滚
noRollbackFor{类.class,类.class}--------让需要回滚的不回滚
Spring整合mybatis
先上代码–起流程都是 dataSouce->sqlSessionFactory->sqlSession(两种)
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.xusx.service"></context:component-scan>
<context:annotation-config></context:annotation-config>
<aop:aspectj-autoproxy expose-proxy="false"></aop:aspectj-autoproxy> 此标签用以开启对于@AspectJ注解风格AOP的支持。
<!--dataSource: 使用spring的数据源代替mybatis的位置 c3p0 dbcp druid
这里使用Spring提供的jdbc
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
<property name="url" value="jdbc:oracle:thin:@//127.0.0.1:1521/orcl"></property>
<property name="username" value="scott"></property>
<property name="password" value="tiger"></property>
</bean>
<!-- 配置 sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<!-- 设置mybatis的配置文件。其次property 也可直接设置 mybatis-configuration的相关配置-->
<property name="configLocation" value="mybatis-config.xml"></property>
</bean>
<!--方式一 配置 SqlSessionTemplate== Sqlsession 单独拿来使用 -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 构造方法设置 -->
<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
</bean>
<!--方式二 赋值给其父类SqlSessionDaoSupport -->
<bean id="empServiceImpl" class="com.xusx.service.EmpServiceImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
两种配置
一:sqlSessionTemplate就是sqlSession 业务类通过构造方法拿到其即可使用
二: 业务类继承 SqlSessionDaoSupport其即可,可以拿到 getSqlSession()
public class EmpServiceImpl extends SqlSessionDaoSupport implements EmpService {
// 方式一:sqlSessionTemplate 注解设置
private SqlSessionTemplate sqlSessionTemplate;
public EmpServiceImpl(SqlSessionTemplate sqlSessionTemplate){
this.sqlSessionTemplate=sqlSessionTemplate;
}
public List<Emp> getEmp() {
// 方式一
sqlSessionTemplate.getMapper(EmpService.class).getEmp();
// 方式二
List<Emp> emp = getSqlSession().getMapper(EmpService.class).getEmp();
return emp;
}
}
JavaWeb 启动时加载ioc容器—ContextLoaderListener
其本质servletContextLister监听加载
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
新注解:
Spring集成Junit测试:
1、导入spring-test坐标
2、@RunWith替换运来的运行期
3、ContextConfiguration 加载配置类或配置文件
4、@AutoWired注入需要测试的对象
5、创建测试方法进行创建