1.Spring框架存在的意义
1.解决了什么问题
Spring是一个开源框架,Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能。同样的功能,在EJB中要通过繁琐的配置和复杂的代码才能够实现,而在Spring中却非常的优雅和简洁。
将创建对象的任务交给IOC容器(注解+反射),使用对象时通过依赖注入实现
2.如何解决的
Spring是一个IOC(DI)和AOP容器框架
Spring的优良特性:
-
1)IOC:说白了就是将创建对象的过程或者说创建对象的权限交给了spring框架来帮进行处理,程序员再不用通过new的方式来手动创建Javabean对象,这个过程就叫做控制反转。其实就是通过反射创建对象
-
2)依赖注入:DI——Dependency Injection,反转控制(IOC)最经典的实现。
- DI(dependency injection) 依赖注入
就是使用spring框架为JavaBean的属性赋值的过程
可以实现解耦:即使用多态——将“接口引用”指向”注入对象”的引用 - IOC与DI的区别:
1:IOC是为我们创建对象。DI是我们创建对象后的属性赋值
2:先有IOC,才能够使用DI
- DI(dependency injection) 依赖注入
-
3)面向切面编程:Aspect Oriented Programming——AOP
- 将公共的代码抽取出来,放到类中(即切面)
- 使用动态代理,应用到代码要使用的地方
-
4)一站式:在IOC和AOP的基础上,可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层(Web层)的SpringMVC和持久层的Spring JDBC)。
Spring中创建bean
1.依据配置文件创建对象
在applicationContext.xml中加入配置,调用无参构造器(或者缺省构造器) 创建对象:
step1:为类添加无参构造器(内部有了这个无参构造的类就可以省略这一步)
step2:配置文件中添加一个bean元素
<!-- 配置UserBean1的初始化 -->
<bean id="userBean1" class="cn.itcast.spring.demo2.UserBean1">
</bean>
step3:启动创建容器,调用容器的getBean方法就是实例化这个类返回对象<—重要思想!!!
public class UserDaoTest {
@Test
public void testHello() {
// 原来创建Bean的方式
// UserDao userDao = new UserDaoImpl();
// userDao.hello();
// ApplicationContext是Spring容器的一个子接口
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDaoImpl userDao = (UserDaoImpl) context.getBean("userDao");
userDao.hello();
System.out.println(userDao.getUsername());
}
}
2.静态工厂
方式二: 用静态工厂方法创建—解决抽象类
待创建的对象
public class UserBean2 {
public void hello() {
System.out.println("Hello Spring UserBean2");
}
}
工厂
public class UserBean2Factory {
public static UserBean2 createUserBean2(){
return new UserBean2();
}
}
applicationContext.xml中的配置
<bean id="userBean2" class="cn.itcast.spring.demo2.UserBean2Factory"
factory-method="createUserBean2">
</bean>
测试:
@Test
public void testUserBean2() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserBean2 userBean2 = (UserBean2) context.getBean("userBean2");
userBean2.hello();
}
3.实例工厂
待创建的对象:
public class UserBean3 {
public void hello() {
System.out.println("Hello Spring UserBean3");
}
}
实例工厂
public class UserBean3Factory {
public UserBean3 createUserBean3(){
return new UserBean3();
}
}
<!-- 通过实例工厂方式创建对象 -->
<!-- 配置实例工程 -->
<bean id="userBean3Factory" class="cn.itcast.spring.demo2.UserBean3Factory">
</bean>
<!-- 配置Bean -->
<bean id="userBean3" class="cn.itcast.spring.demo2.UserBean3"
factory-bean="userBean3Factory" factory-method="createUserBean3">
</bean>
@Test
public void testUserBean3() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserBean3 userBean3 = (UserBean3) context.getBean("userBean3");
userBean3.hello();
}
4.基于注解的方式配置bean
1.常用的注解
-
@Component:标识一个普通组件
-
@Repository:标识一个持久化层的组件
-
@Service:标识一个业务逻辑层的组件
-
@Controller:标识一个表现层的组件
-
@Autowired:
- 设置类中需要自动装配的属性
- 添加了该注解的属性默认必须装配成功,否则会抛出异常
- 如果要设置某个属性可以不装配,可以设置该注解中的required的属性值是false
-
@Qualifier
- 通过该注解的value属性设置根据哪个id实现自动装配
2.使用注解的方式配置bean的步骤:
1)在类上添加对应的注解
- 不带属性的类
/*
* 添加了注解的类会交给Spring的IOC容器管理,默认在IOC容器中的bean的id是类的首字母小写,
* 我们也可以通过注解的value属性来指定该id,value的属性名可以省略
*/
//@Repository(value="userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
- 带属性的类
@Service("userService")
public class UserServiceImpl implements UserService {
/*
* 自动装配的步骤:
* 1.根据属性的类型实现自动装配
* 2.以属性名作为id从IOC容器中寻找
* 3.我们还可以通过@Qualifier注解的value属性来指定根据那个id实现自动装配
*
*/
@Qualifier(value="userDao2")
@Autowired
private UserDao userDao;
/*
* 添加了@Autowired注解的属性默认必须装配成功,否则会抛出异常,
* 我们可以通过指定@Autowired注解中的required属性值为false来告诉Spring当前属性可以不装配
*/
@Autowired(required=false)
private User user;
2)在Spring的配置文件中设置自动扫描的包
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 设置自动扫描的包
base-package属性:设置一个基础包,Spring会自动扫描该包及其子包
-->
<context:component-scan base-package="com.mytest.spring.annotation"></context:component-scan>
</beans>
1.spring的bean容器相关的注解
1)@Autowired 是我们使用得最多的注解,其实就是 autowire=byType 就是根据类型的自动注入依赖(基于注解的依赖注入),可以被使用再属性域,方法,构造函数上。
2)@Qualifier 就是 autowire=byName, @Autowired注解判断多个bean类型相同时,就需要使用 @Qualifier(“xxBean”) 来指定依赖的bean的id:
案例:
@Controller
@RequestMapping("/user")
public class HelloController {
@Autowired
@Qualifier("userService")
private UserService userService;
......
}
3)@Resource 属于JSR250标准,用于属性域额和方法上。也是 byName 类型的依赖注入。使用方式:@Resource(name=“xxBean”). 不带参数的 @Resource 默认值类名首字母小写。
4)JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singleton)。@Inject就相当于@Autowired, @Named 就相当于 @Qualifier, 另外 @Named 用在类上还有 @Component的功能。
5)@Component, @Controller, @Service, @Repository, 这几个注解不同于上面的注解,上面的注解都是将被依赖的bean注入进入,而这几个注解的作用都是生产bean, 这些注解都是注解在类上,将类注解成spring的bean工厂中一个一个的bean。@Controller, @Service, @Repository基本就是语义更加细化的@Component。
6)@PostConstruct 和 @PreDestroy 不是用于依赖注入,而是bean 的生命周期。类似于 init-method(InitializeingBean) destory-method(DisposableBean)
2.spring中注解的处理
spring中注解的处理基本都是通过实现接口 BeanPostProcessor 来进行的:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
相关的处理类有:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
这些处理类,可以通过 context:annotation-config/ 配置隐式的配置进spring容器。这些都是依赖注入的处理,还有生产bean的注解(@Component, @Controller, @Service, @Repository)的处理:
<context:component-scan base-package="com.mytest.spring.helloworld"/>
这些都是通过指定扫描的基包路径来进行的,将他们扫描进spring的bean容器。注意context:component-scan也会默认将 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor 配置进来。所以context:annotation-config/是可以省略的。另外context:component-scan也可以扫描@Aspect风格的AOP注解,但是需要在配置文件中加入 aop:aspectj-autoproxy/ 进行配合。
Spring获取bean的方式
1)根据bean的名称(id属性值)获取
2)根据bean的类型获取,但要保证IOC容器中该类型的bean是唯一的,否则会抛出异常
/*
* ★获取bean的方式:
* 1)根据bean的名称(id属性值)获取
* 2)根据bean的类型获取
* -注意:一定要保证IOC容器中该类型的bean是唯一的,否则会抛出异常
*/
//根据bean的名称(id属性值)获取
HelloWorld helloWorld = (HelloWorld) ioc.getBean("helloWorld");
//根据bean的类型获取
HelloWorld helloWorld2 = ioc.getBean(HelloWorld.class);
Spring的作用:
a.简化开发:spring对常用的API都做了一些简化和封装(比如,用spring jdbc访问数据库,就不用考虑如何获取连接和关闭连接)。
b.解耦:spring帮我们管理对象的依赖关系,这样对象间的耦合度低,方便维护。
c.集成其他框架:spring可以将其他的框架集成进来。(比如可以将Mybatis等框架集成进来)
1.1 ioc是什么?
IOC——:inversion of control,即“控制反转”,ioc不是一种技术,是一种设计思想,一个重要的面向对象编程的法则。在java开发中,IOC意味着将你设计好的对象交给容器控制,而不是在传统的在你的对象内部直接控制。
何为“控制”:传统的JAVA SE程序设计,我们是从对象内部通过new创建对象,是程序控制对象的创建,而IOC有一个专门的容器(IOC容器)创建对象,IOC容器控制外部资源的获取(包括对象,文件等)。
何为“反转”:传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,即“正转”。而“反转”,是由容器来帮我们去创建及注入依赖对象。对象只是被动的接受依赖对象。依赖对象的获取被反转了。
1.2 ioc有什么用?
ioc容器能帮我们管理对象的依赖关系,这样对象与对象的耦合度低,方便代码的维护。对于spring框架来说,ioc就是由spring来负责控制对象的生命周期和对象间的关系。
1.3 DI
DI——dependenc injection,即“依赖注入”。IOC与DI有什么关系呢?其实它们是同一个概念的不同角度描述。“依赖注入”,相对ioc而言,“依赖注入”明确地描述了“被注入对象”依赖ioc容器配置依赖对象。IOC的一个重点是在系统运行中,动态的向某个对象提供所需要的其他对象,这一点通过DI实现。DI如何实现?这就要引入java 1.3之后的一个重要特征——反射(reflection)。它允许程序在运行的时候动态生成对象、执行对象的方法、改变对象的属性。spring就是通过反射注入的。
2.1 AOP?
2.1.1 Aop是什么?
AOP——(Aspect-Oriented Programming),即面向切面编程。AOP可以说是OOP的补充和完善,是Spring框架核心功能之一。
2.1.2 AOP的特点(优势)?
为了解决当我们需要为分散的对象引入公共行为的时候,如程序中交叉业务逻辑(系统日志,事务处理,系统安全验证,系统数据缓存等等),这是我们应当把它封装成一个切面,注入到目标对象(具体逻辑)中去。可以简化代码开发和效率,能够在不改变原有代码的基础上扩展新的功能实现。
2.1.3AOP 核心概念、术语
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。其相关概念术语如下:
切面(aspect): 横切面对象,一般为一个具体类对象(本质就是加了@Aspect注解的类)
通知(advice):拦截到连接点之后只要执行的方法
切入点(pointcut):对连接点拦截内容的一种定义
连接点(joinpoint):程序执行过程中某个特定的点,一般指被拦截到的的方法
目标对象(target):代理的目标对象。
通知(Advice):在切面的某个特定连接点上执行的动作,例如before,after等
知识点术语强化:
- 切面(可以简单理解为要植入的新的业务功能,这个功能交给某个类负责,这个类就是切面)
- 通知(可以简单理解为一个业务中的扩展逻辑的若干步骤,例如先做什么(before),再做什么(afterReturn),最后做什么)
- 切入点(在原有的哪些业务方法上扩展新的业务,可以将切入点理解为方法的集合)
- 连接点(可以简单理解为切入点中的一个具体方法)
- 目标对象(需要扩展功能的那个对象,一般为被代理对象)
- 代理对象(负责调用切面中的方法为目标对象植入新的功能)
2.1.4 Spring AOP 的编程实现
Spring中AOP代理由Spring的IOC容器负责生成、管理。
其依赖关系也由IOC容器负责管理。
因此,AOP代理可以直接使用容器中的其它bean实例作为目标,
这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:
1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了
2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理。
AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:
- 定义普通业务组件(切面)
- 定义切入点,一个切入点可能横切多个业务组件
- 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。
所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理
3.还有什么可改进之处