使用idea搭建webapp工程">
使用idea搭建webapp工程- 1. 程序开发的不断演进
- 2. 控制反转(IOC)
- 3. 依赖注入(DI)
- 4. Spring工厂类的认识
- 5. Spring的bean管理
- 6. 属性的注入方式
- 7. 切面编程(AOP)
- 8. 事务管理
重点地方使用蓝色标记
笔记是基于
spring4
的版本程序设计的基本规则:open-close原则,程序的扩展是open的,但是程序的修改是close的,即尽量不修改程序的源码,实现对程序的扩展。
Processor
使用idea搭建webapp工程
https://class.imooc.com/lesson/1271#mid=28790
1. 程序开发的不断演进
传统开发方式,new对象
- 代码示例:
UserServiceImpl us = new UserServiceImpl();
- 缺点:没有面向接口编程
什么需要面向接口开发? 从系统稳定的角度思考,为什么在一个类中实例化一个别的对象是不稳定的,例如
Student s = new Student()
,因为Student
是一个具体的类是可以随意变化的,而接口是对行为的抽象,抽象是不可变化的是稳定的,随意我们需要面向接口编程
- 代码示例:
面向接口编程 + 工厂模式
- 代码示例:
- 控制层
UserService us = new UserServiceImpl_1();
- 控制层
UserService us = new UserServiceImpl_2();
- 控制层
- 缺点:控制层/业务层与服务层代码耦合了,例如想要修改实现类,就必须动源代码才能实现
- 代码示例:
- 引入工厂模式
```java
1.需求:controller调用UserService接口的方法
public PassportController{
}public void getUser(){
UserFactory.get("YiHua").addUser();
}
2.缺点:这个时候虽然控制层可以通过工厂获取想要的实现类对象,但是依然存在代码之间的耦合 a.例如控制层需要什么样子的实现类,需要事先指明,如上面的”YihHua” b.新增一个实现类,需要给工厂类新增一个实现方法
4. 工厂 + 反射 + 配置文件
```java
1.控制层需要声明实现的接口
2.代码走使用接口的地方,到工厂通过加载配置文件,反射获取到对象实例
3.实例对象传递到控制层
直接说:想要解决,上述的流程的弊端,必须要舍弃程序员事先设定好流程这个弊端,所以要将这个控制权交给一个容器,那就是spring
c. IDI表面上是如何避免问题呢?
ⅰ. 控制层只需要声明需要的接口,DI在运行到需要使用到声明的接口的时候就会创建它的对象
2. 控制反转(IOC)
https://class.imooc.com/lesson/729#mid=18082
控制反转的概念:就是原本在程序中手动创建对象的权利,交给spring容器来管理
3. 依赖注入(DI)
依赖注入的概念:Spring在创建对象的过程中,将这个对象锁依赖的属性注入进去
使用的设计模式:工厂模式、观察者模式
4. Spring工厂类的认识
- 上图是
spring
工厂类的继承实现图,绿色是实现类、淡绿色是抽象类、紫色是接口,整体从上向下的结构 - BeanFactory:老版的工厂,spring3、spring2、spring1
- ApplicationContext:新版本的工厂,spring4的,由于spring5还没学习暂时不清楚
- ClassPathXmlApplicationContext:加载我们类路径下面的Spring的配置文件
- FileSystemXmlApplicationContext:加载文件系统中的配置文件,加载磁盘路径下的配置文件
新旧工厂的一个区别?
spring在启动时读取Bean的配置信息
- 在spring容器中生成一份相对应的Bean配置注册表
- 根据Bean注册表去实例化Bean
- 将实例化的Bean实例放到Spring容器提供的Bean缓冲池中
-
5.2 Bean的实例化方式
使用类的构造器实例化(默认使用无参构造),只需要给要被管理的bean提供无参构造即可 ```java 1.提供一个需要被管理的bean,包含无参构造 public class User{ public User(){} }
2.配置文件
3.测试类 @Test public void test() { // 创建Spring的工厂 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”); // 通过工厂获得类,在这一步user会被实例化 User user = (User)applicationContext.getBean(“user”); }
2. 使用静态工厂的方法实例化(简单工厂模式)
```java
1.提供一个需要被管理的bean
public class User{}
2.构建静态工厂
public class UserFactory{
public static User createUser(){
System.out.println("user被实例化了");
return new User();
}
}
3.配置文件
<bean id="userFactory" class="com...UserFactory" factory-method="createUser"/>
4.测试类
@Test
public void test(){
// 创建Spring的工厂
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类,在这一步user会被实例化
User user = (User)applicationContext.getBean("userFactory");
}
- 使用实例工厂方法实例化(工程方法模式),和静态工厂的区别是是否加static ```java 1.提供一个需要被管理的bean public class User{}
2.构建静态工厂 public class UserFactory{ public User createUser(){ System.out.println(“user被实例化了”); return new User(); } }
3.配置文件
4.测试类 @Test public void test(){ // 创建Spring的工厂 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”); // 通过工厂获得类,在这一步user会被实例化 User user = (User)applicationContext.getBean(“userFactory”); }
<a name="xe2bj"></a>
### 5.3 Bean的常见配置
> 注意这里的配置,其实在@Bean注解中都是有对应的属性字段,所以看下开注解开发其实和xml配置是一样的,学习到这里一定要上升一个高度来理解
```java
<bean id="user" name="user" class="com...User" scope="singleton"/>
1.id: 通过指定一个id属性指定Bean的名称,- id属性在IOC容器中必须是唯一的
2.name: 和id一样的含义但是和id只能取其一,如果Bean名称中含有特殊字符,就需要使用name属性
3.class: 用于设置一个类的完全路径名称,主要作用是IOC容器的生成类实例
4.scope: 是Bean的作用域,决定了我们IOC容器中对象创建时是单例还是多例,它有四个属性值
- singleton: 默认、单例;对象只会在IOC容器初始化的时候创建一次,且全局唯一
- prototype: 多例,说明当前对象是多例模式,每一次调用getBean它都会创建对象
- request: 表示对同一个请求创建一个对象,也就是说会为每一个Http请求创建一个Bean实例,只适用于webApplicationContext环境下
- session: 在同一个会话创建一个对象,也就是说会为每一个session创建一个Bean,同一个http session共享一个bean,
- 注意: request、session都需要引入springMVC,也就是需要web环境,为什么需要web也好理解,http和session都是web环境下的
5.lazy-init: 它是延迟加载,也就是我们常说的懒加载。
- default: todo
- false: 表示加载完成配置,生成工厂对象就会为配置中的bean生成实例,这里也对应了新版本的对象工厂ApplicationContext,在加载配置的时候就生成bean,设置为true就可以遏制
- true: 表示当我们需要这个bean的时候才会进行实例化的操作;值得注意的是这时候scope必须为singleton单例的不然它会失效
- 使用延迟加载的好处: 第一次getBean的时候才会被创建,可以起到减少服务器压力。比如说当我们有1000个类都被spring管理,设置这个属性说明当我们需要这个类的时候才会被加载,这对我们系统启动的速度是有帮助的。
5.4 Bean的生命周期
5.4.1 生命周期方法
<bean id="user" class="com...User" init-method="init" destory-methd="destory"/>
1.init-method: 当bean被载入到spring容器的时候,就会调用init,注意这里说的被载入到容器中调用,所以对应了声明周期中其中init方法在很靠后面执行的
2.destory-methd: 当bean在容器中被删除的时候调用destory方法,设置销毁方法的bean必须是单例的,多例的话spring是无法确定销毁哪一个实例的
5.4.2 生命周期的流程
流程中没有强调”bean实现”,这样的字段就说明不是bean实现 下面的个人理解一定注意有些步骤,如果没实现,也就是扯淡 个人理解 ApplicationContext在加载配置文件的时候,初始化bean执行到第7步骤,就将实例加载到bean的缓存池 当系统需要调用某个实例的方法的时候,例如
user.getname()
,执行到10步骤 11步的只有执行close方法销毁实例才会执行
- 实例化
bena
、对象实例化,instantiate bean
- 填充属性、封装属性,
populate properties
- 如果
bean
实现了BeanNameAware
执行setBeanName
,这样可以让bean知道我们在配置文件中配置的id
名称 - 如果
bean
实现了BeanFactoryAware
或者ApplicationContextAware
设置工厂setBeanFactory
或者上下文对象setApplicationContext
,这个可以让bean了解工厂的信息 - 如果实现了后处理bean
(BeanPostProcessor)
执行postProcessBeforInitialization
- 如果
Bean
实现InitializingBean
执行afterPropertiesSet
- 如果存在则调用init-
method
指定的初始化方法 - 如果实现了后处理bean
(BeanPostProcessor)
,执行postProcessAfterInitialization
- 执行业务,调用类中的方法,这个需要对象显示的调用,例如
user.getname()
- 如果
bean
实现了DisposableBean
执行destory
- 如果存在调用执行的销毁方法
destory-method
5.4.3 后处理bean
和aop的区别是,aop是对请求的增强
6. 属性的注入方式
6.1 xml配置方式
6.2 注解的方式
6.2.1 定义类被spring管理
1.spring2.5引入使用注解去定义bean
2.@Component: 将类交给spring管理
3.@Controller: 对控制层/视图层标注
4.@Service: 服务层
5.@Repository: dao层、mapper层
6.2.2 属性注入的注解
一、@Value注解的使用,注意1、3常用,并且就算是读取yml文件也是一样的方式
1.直接使用
@Value("yihua")
private String name;
2.提供set方法
private string name;
@Value("yihua)
public void setName(String name){
this.name = name;
}
3.为静态变量注入
private static string name;
@Value("yihua")
public void setName(String name){
User.name = name;
}
二、@Resource注解的使用
......
三、两者的比较和使用技巧
1.它们俩都可以用来注入Bean
2.@Autowired:是Spring提供的注解。它的策略是按照类型装配默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false。当一个类型有多个bean值的时候,会造成无法选择具体注入哪一个的情况,这个时候我们需要配合着@Qualifier使用,这样可以通过类型和名称定位到我们想注入的对象
3.@Resource:是J2EE提供的注解
4.@Autowired和@Resource都是注入属性注解,@Resource是J2EE的注解能减少与spring之间的耦合。
四、匹配逻辑
1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
2. 如果指定了名称name,则从上下文中查找名称(id或name)匹配的bean进行装配,找不到则抛出异常
3. 如果指定了类型type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
6.2.3 其它常见注解
1.配置文件中的init-method方法声明,使用@PostConstruct
2.配置文件中的destory-method方法声明,使用@PreDestory
3.配置Bean的作用域scope,使用@Scope
7. 切面编程(AOP)
AOP即面向切面编程,它采取横向抽取的机制,取代了传统纵向继承体系的重复代码
7.1 基于AspectJ的AOP开发
逻辑控制:由AopProxyFactory
根据AdvisedSupport
对象的配置来决定,默认策略:如果目标类是接口实现类,就采用jdk
动态代理实现,接口 - 否则cglib
7.2 动态代理
7.2.1 Jdk的动态代理
jdk的动态代理的实现完全是基于jdk自带的包
jdk动态代理: 只可以对实现接口类的进行动态代理
Jdk动态代理的核心:Proxy 和 invocationhandler(程序执行者)
通过内部的反射机制实现的,反射机制在生成类的时候比较高效
7.2.2 cglib的动态代理
cglib动态代理: 非常底层的字节码文件, 它是通过子类的方式,也就是是生成一个类来继承这个需要代理的类
cglib以继承的方式动态生成目标代理类,借助ASM实现,ASM在生成类后的执行效率比较高效,且我们可以将生成的类进行缓存
动态代理:https://class.imooc.com/lesson/1240#mid=29740
动态代理和静态代理的区别
8. 事务管理
8.1 概念理解
总说:所谓声明式事务是指,在执行方法时自动开启或者关闭事务的技术。它底层是利用了AOP来实现了自动开启、自动提交、自动回滚事务
分说:它的使用规则非常简单,在要使用事务的方法上加@Transactional注解,则会进入方法时自动打开事务,如果目标方法运行成功则事务自动提交。如果在运行时抛出运行时异常或者继承它的子类事务才会发生回滚或者终止
8.2 事务传播级别
- never:
- 禁止方法声明事务,如果存在事务则抛出异常
- not_supported:
- 该方法不使用事务的形式执行;
- 如果当前有事务,则将该事务挂起,自己不使用事务去运行数据库操作
- supports:
- 如果当前有事务,则使用事务;
- 如果没有事务则不使用事务;多用于查询
- required:
- 如果当前的方法没有或者不存在事务,会创建一个新的事务;
- 如果方法已经存在事务,则加入到原来已经存在的事务中;
- 比如方法本身没有事务,但是调用方申明了事务,则事务会传递到被调用方;
- 多用于增、删、改
- mandatory:
- 必须强制有一个事务,理解为调用方必须支持事务,否则抛出异常
- requires_new:
- 核心是“会产生2个事务各自提交”
- 如果当前有事务,则挂起该事务,创建一个新的事务给自己使用
- 如果当前没有事务则和required相同
- nested:
- 如果当前不存在事务则等于require;
- 如果当前有事务,会产生一个嵌套的事务,嵌套事务是独立提交或者回滚
- 如果主事务提交,会携带子事务一起提交
- 如果主事务回滚,会携带子事务一起回滚;子事务异常,主事务可以回滚也可以不回滚
- 领导决策不对,小弟跟着受罪;小弟犯错,领导可以推卸责任,也可以拉小弟一把
@Autowired
@Rouce...
// 使用@Autowired会有一个警告,变成这样就ok啦
private final StringRedisTemplate redisTemplate;
public RedisUtil(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
下面是idea设置忽略这个警告的配置,记一下