spring 控制反转
控制权的转移,由spring负责对象的创建和维护。
注入方式
自动装配:@Autowired
此处暂略
setter方法注入
需要创建bean的成员对象的setter方法
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<!--配置当前类中相应的属性-->
<property name="springDao" ref="springDao"></property>
</bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
构造注入
注入带参数构造函数的bean
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<!--创建构造器注入,如果主类有带参的构造方法则需添加此配置-->
<constructor-arg ref="springDao"></constructor-arg>
<constructor-arg ref="user"></constructor-arg>
</bean>
<bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean>
<bean name="user" class="com.bless.springdemo.vo.User"></bean>
静态工厂的方法注入
通过调用静态工厂的方法来获取自己需要的对象
<bean name="springAction" class="com.bless.springdemo.action.SpringAction" >
<!--(3)使用静态工厂的方法注入对象,对应下面的配置文件(3)-->
<property name="staticFactoryDao" ref="staticFactoryDao"></property>
</property>
</bean>
<!--此处获取对象的方式是从工厂类中获取静态方法-->
<bean name="staticFactoryDao" class="com.bless.springdemo.factory.DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean>
实例工厂的方法注入
获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法
<bean name="springAction" class="com.bless.springdemo.action.SpringAction">
<!--(4)使用实例工厂的方法注入对象,对应下面的配置文件(4)-->
<property name="factoryDao" ref="factoryDao"></property>
</bean>
<!--此处获取对象的方式是从工厂类中获取实例方法-->
<bean name="daoFactory" class="com.bless.springdemo.factory.DaoFactory"></bean>
<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean>
bean的作用域
分别为singleton、prototype、request、session、global session
bean的作用域由scope属性设置,默认singleton
单例:singleton
每个容器只存在一份
prototype
每次向ioc容器获取bean时创建新的实例
request
每次http请求创建一个实例且仅在当前request内有效
session
该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
global session
该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例
bean的生命周期
定义、初始化、使用、销毁
定义
通过构造器或工厂方法创建Bean实例,为Bean的属性设置值或对其它Bean的引用
初始化
调用bean初始化方法
使用
用户从spring ioc容器中获取实例并使用
销毁
调用bean销毁方法
<!--方式1:xml配置init-method属性和destory-method属性,执行bean对于的init和destory方法-->
<bean id="person" class="com.test.Person" init-method="init" destroy-method="destory">
<property name="name" value="xujian"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
public class TestInitializingBean implements InitializingBean,DisposableBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("run InitializingBean");
}
@Override
public void destory() throws Exception {
System.out.println("run destory");
}
}
注解
类的自动检测
<!--检测类并注册bean,context:component-scan包含了context:annotation-config-->
<context:component-scan base-package="com.xyg.pojo"></context:component-scan>
作用域:@Scope
@Component
@Scope("prototype")
public class AnnotationTest {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void getName(){
System.out.println("调用方法");
}
}
自动装配:@AutoWired
//setter注入
@Component
public class AutoService {
AutoDao autoDao;
@Autowired
public void setAutoDao(AutoDao autoDao) {
this.autoDao = autoDao;
}
public void say(String message){
autoDao.say(message);
}
}
//成员注入
@Component
public class AutoService {
@Autowired
AutoDao autoDao;
public void say(String message){
autoDao.say(message);
}
}
@Qualifier
按类型装配可能有多个bean实例情况,可使用Qualifier缩小范围
@Service("service")
public class EmployeeServiceImpl implements EmployeeService {
public EmployeeDto getEmployeeById(Long id) {
return new EmployeeDto();
}
}
@Service("service1")
public class EmployeeServiceImpl1 implements EmployeeService {
public EmployeeDto getEmployeeById(Long id) {
return new EmployeeDto();
}
}
//成员上的Qualifier
public class EmployeeInfoControl {
@Autowired
@Qualifier("service")
EmployeeService employeeService;
}
@Bean
@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名
定义一个bean
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
等价于
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
自定义bean的命名
默认情况下bean的名称和方法名称相同,你也可以使用name属性来指定
@Configuration
public class AppConfig {
@Bean(name = "myFoo")
public Foo foo() {
return new Foo();
}
}
接受生命周期的回调
public class Foo {
public void init() {
// 初始化逻辑
}
}
public class Bar {
public void cleanup() {
// 销毁逻辑
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}
AOP面向切面编程
概念
是一种编程范式(其他变成范式:面向过程、面向函数)
目的
DRY:解决代码重复问题
SOC:关注点分离
Spring AOP注解
@Aspect
表示该类是一个切面的配置类
@pointcut
表示那些类那些方法上来植入代码,由切面表达式组成
@Pointcut("@annotation(AdminOnly)")
public void adminOnly(){
}
Advice
包括@Before,@After等,表示在方法的之前/之后进行代码的植入
切面表达式
格式:@Pointcut("切入点表达式"),方法体通常是空的。
匹配包/类型
@Pointcut("within(com.xyg.Service.UserService)")
public void matchType(){}
匹配对象
@Pointcut("this(com.xyg.DemoDao)")
public void thisDemo(){}//匹配指定类型的方法
@Pointcut("target(com.xyg.IDao)")
public void targetDemo(){}//匹配接口
@Pointcut("bean(*Service)")
public void BeanDemo(){}//匹配以Service结尾的Bean
execution表达式
1、execution(): 表达式主体。
2、第一个号:表示返回类型,号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个号:表示类名,号表示所有的类。
5、(..):最后这个星号表示方法名,号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
例子
@Component
public class AdviceDao {
public void say(){
System.out.println("原始方法");
}
}
@Aspect
@Component
public class AdviceTest {
//前置切点,值可以切点引用,也可以直接写切点表达式
@Before("execution(* com.how2java.dao.AdviceDao.*(..))")
public void before(){
System.out.println("前置通知。。。。。");
}
@Around("execution(* com.how2java.dao.AdviceDao.*(..))")
public void around(ProceedingJoinPoint point) throws Throwable{
System.out.println("环绕通知前。。。。");
point.proceed();
System.out.println("环绕通知后。。。。");
}
@AfterReturning("execution(* com.how2java.dao.AdviceDao.*(..))")
public void after(){
System.out.println("后置通知。。。。。");
}
}
对应的xml表示
<!-- 开启对aop注解的支持 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--声明aop配置-->
<aop:config>
<aop:pointcut expression="execution(* com.how2java.dao.AdviceDao.*(..))" id="advicePoint"/>
<!--定义通知类型-->
<aop:aspect id="adviceAspect" ref="advice">
<aop:after-returning method="after" pointcut-ref="advicePoint"/>
<aop:around method="around" pointcut-ref="advicePoint"/>
<aop:before method="before" pointcut-ref="advicePoint"/>
</aop:aspect>
</aop:config>
Spring对代理模式的选择
如果目标对象实现了接口用JDK动态代理,没有实现接口用cglib进行动态代理,如果实现了接口且强制cglib代理,则使用cglib代理。
springMVC原理
客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Moder)->将得到视图对象返回给用户
版本2: SpringMVC是Spring中的模块,它实现了mvc设计模式的web框架,首先用户发出请求,请求到达SpringMVC的前端控制器(DispatcherServlet),前端控制器根据用户的url请求处理器映射器查找匹配该url的handler,并返回一个执行链,前端控制器再请求处理器适配器调用相应的handler进行处理并返回给前端控制器一个modelAndView,前端控制器再请求视图解析器对返回的逻辑视图进行解析,最后前端控制器将返回的视图进行渲染并把数据装入到request域,返回给用户。
DispatcherServlet作为springMVC的前端控制器,负责接收用户的请求并根据用户的请求返回相应的视图给用户。
spring事务
事务隔离级别
隔离级别 | 含义 |
---|---|
DEFAULT | 使用后端数据库默认的隔离级别(spring中的默认选择项) |
READ_UNCOMMITED | 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITTED | 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 |
Mysql 默认隔离级别 REPEATABLE_READ,Oracle 默认隔离级别 READ_COMMITTED
事务的传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务 |
PROPAGATION_REQUIRED_NEW | 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行 |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager |
PROPAGATION_MANDATORY | 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常 |
PROPAGATION_NEVER | 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常 |
PROPAGATION_NESTED | 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务 |
主要分3大类
1)PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。
事务默认传播行为 REQUIRED。最常用的。
2)PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
不会支持原来的事务 ,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。
常用的事务传播行为:PROPAGATION_REQUIRES_NEW
3) PROPAGATION_NESTED
嵌套事务 ,只对DataSourceTransactionManager有效 ,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点
【面试题】REQUIRED、REQUIRES_NEW、NESTED 区分
REQUIRED:只有一个事务(默认,推荐)
REQUIRES_NEW:存在两个事务,如果事务存在,挂起事务,重新又开启了一个新的事务
NESTED:嵌套事务,事务可以设置保存点,回滚到保存点 ,选择提交或者回滚