Inversion Of Control 反转控制
框架IOC就是用来提供一种服务:对系统中的Bean对象的生命周期进行管理
Bean对象的生命周期,就是对象的创建、初始化、使用和销毁的过程
默认是第一次访问时,创建Servlet对象,也可以服务器启动时创建,需要设置load-on-startup标签
load-on-startup的取值:大于等于0,表示服务器启动时创建,值越小,优先级别越高。如果小于0,表示第一次访问时创建Servlet对象
Servlet对象是单例的,避免了多线程资源共享的问题
生命周期方法:
构造方法 → init方法 → service方法【doGet/doPost】 → destroy方法
当服务器启动时,创建Filter对象,并初始化
生命周期方法:
构造方法 → init方法 → doFilter方法 → destroy方法
以ServletContextListener为例:
其用于监听ServletContext对象的创建和销毁
使用示例:
⑴ 可以在其contextInitialized方法中
public void contextInitialized(ServletContextEvent sce);
通过ServletContextEvent对象,得到ServletContext对象,并得到contextPath(/项目名),然后再放到application域中,设置键为PATH。这样在jsp页面通过
${PATH}
得到contextPath,而不必在通过
${pageContext.request.contextPath}
来得到contextPath。减少了代码量
⑵ 统计网站的在线人数,在application域中,放一个统计的参数。根据session的创建和销毁,来更新统计到的在线人数
首先是Listener,其次是Filter,最后是Servlet(设置了load-on-startup)
Spring的IOC容器创建的Bean对象,默认是单例的,而且在IOC容器初始化时,就被创建
⑴ Spring创建Bean对象,默认是单例的【scope=”singleton”】
但可以在核心配置文件中通过bean标签的scope属性来修改为多例的:
scope="prototype"
⑵ 可以在Bean对象中,增加初始化方法和销毁方法
方法定义规则:
① 必须是无参的方法,否则运行时报错:
BeanDefinitionValidationException: Couldn’t find an init method named ‘自定义的init方法名’ on bean with name ‘Bean类’
② 方法的修饰符、返回值类型、抛出的异常无要求
⒈ 通过Bean标签的init-method属性,来添加初始化方法
init-method="方法名"
⒉ 通过Bean标签的destroy-method属性,来添加销毁方法
destroy-method="方法名"
Dependency Injection 依赖注入
IOC是一种思想,而DI则是这种思想的具体实现
Set方法注入:当前Bean对象所需要的关联对象(属性),通过set方法进行传递赋值
构造方法注入:当前Bean对象所需要的关联对象(属性),通过构造器进行传递赋值
接口注入:当前Bean对象需要实现特定的接口,通过接口的方法进行传递赋值
Spring框架支持Set方法注入和构造方法注入
不用接口注入,是因为这样做有侵入性,即造成了对接口的依赖性
⑴ 8个基本数据类型,String类型的属性赋值
⑵ 引用数据类型的属性赋值【引用其他Bean】
或
⑶ 引用数据类型的属性赋值【引用内部Bean】
注意:因为内部bean对象,就是给当前属性赋值用的。而其他bean无法使用,所以无需定义id
⑴ 根据构造方法的形参的名字,赋予对应的值
⑵ 通过index属性,指定参数的位置
注意:index从0开始
⑶ 通过形参类型,区分重载的构造方法
注意:形参类型和属性声明的类型要一致
xmlns:p="http://www.springframework.org/schema/p"
ns即Namespace(名称空间)的简称
Tips:可以点击Namespaces,勾上p,即可
该list最终是一个ArrayList集合
该map最终是一个LinkedHashMap
值
值
值
xmlns:util="http://www.springframework.org/schema/util"
同时会在 xsi:schemaLocation中增加:
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"
Tips:可以点击Namespaces,勾上util,即可
该list最终是一个ArrayList集合
该map最终是一个LinkedHashMap
当bean对象的某个属性是引用数据类型时,可以通过property标签来覆盖刚才赋值的对象的属性
Tips:静态类(静态工厂),属性可以为map集合,并通过静态初始化块,来给map集合元素
注意:静态工厂只会被加载一次,它不会被实例化,所以map集合在内存中只有一个
Tips:实例工厂,属性可以为map集合,并通过构造方法,来给map集合添加元素
注意:实例工厂只会被实例化一次,所以map集合在内存中只有一个
FactoryBean是一个接口,用于管理Bean。其支持项目功能的扩展,例如和其他框架整合。
Spring框架利用FactoryBean接口的实现类,来集成第三方框架
public interface FactoryBean {
T getObject() throws Exception;
Class> getObjectType();
boolean isSingleton();
}
isSingleton 方法:用于设置获取到的bean的实例是单例的(true),还是原型(false)
实现FactoryBean接口,声明泛型,并实现三个抽象方法。在核心配置文件中配置id和class,通过id获取bean的实例
Tips:子类bean标签,可以不写class,因为可以通过父类bean标签继承过来。同时还会继承父bean标签的属性
Tips:抽象标签,需要添加abstract属性为true,可以不写class属性。其属性值会被子bean继承
Tips:
⑴ 虽然核心配置文件是自上而下解析,但是因为上面的bean标签添加了depends-on属性,所以会先创建被依赖的bean对象,再创建依赖其他bean的bean对象
⑵ 如果在依赖和被依赖的bean标签中间,有其他的bean标签,则其实例会在最后被创建。即中间的bean的实例,在依赖其他bean标签的bean对象创建之后创建
scope参数:
⑴ prototype:原型。每次都创建一个新的bean对象,并且是在getBean的时候创建bean的实例
⑵ singleton:单例。只会创建一个bean对象,每次获取到的都是同一个对象。它是在解析核心配置文件的时候创建实例
取值为true或false
false:在解析配置文件的时候创建对象【默认】
true:在获取对象的实例时,创建对象
原型,在获取实例时创建对象
单例,在获取实例时创建对象
Tips:如果设置scope参数为prototype,即为bean的实例不是单例的,则此时lazy-init参数为true。即使此时设置为false,也是无效的
注意:方法必须是无参的,方法的修饰符、返回值类型、有无异常抛出无要求
bean的生命周期:
构造方法 → 初始化方法 → bean对象的方法 → 销毁方法
对Bean对象的初始化方法进行拦截,在初始化方法前,后执行功能扩展
如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后,添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
postProcessBeforeInitialization方法:在bean对象的初始化方法之前被执行
postProcessAfterInitialization方法:在bean对象的初始化方法之后被执行
Tips:后置连接器,对所有在核心配置文件中配置过的,bean对象都有效,即使没有初始化方法,两个方法也会被执行
注意:如果没有在bean标签中声明init-method,则初始化方法即使有,也不会被执行
实现BeanPostProcessor接口,并在核心配置文件中声明即可
【主要是引用c3p0的properties配置文件】
引用class路径下的配置文件
需要引入context名称空间
xmlns:context="http://www.springframework.org/schema/context"
同时会在 xsi:schemaLocation中增加:
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"
【jdbc.properties】
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///
jdbc.user=root
jdbc.password=???
【核心配置文件】
【测试类】
@Test
public void test() throws SQLException {
DataSource source = (DataSource) ioc.getBean("dataSource");
Connection connection = source.getConnection();
System.out.println(connection);
connection.close();
}
需要设置bean标签的autowire属性
autowire属性的可选值:
⑴ byType 根据属性所属的引用数据类型来赋值
⑵ byName 根据属性值对应的set方法,去掉set,首字母小写,来赋值
根据属性所属的类型,来赋值
通过这种方式,可以不用给属性所属的bean标签赋予id属性
注意:通过byType方式,所装配的bean只能有一个。否则运行报错:NoUniqueBeanDefinitionException
根据属性所对应的set方法,去掉set,首字母小写,来赋值
通过这种方式,就需要给属性所属的bean标签赋予id属性
注意:
⑴ 如果找不到属性类型对应bean,则赋值为null,而不是报错
⑵ 自动装配只能给自己定义的类型的属性赋值
Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图
SpEL使用#{ } 作为定界符,所有在大框号中的字符都被认为是SpEL表达式
#{八种基本数据类型}
#{字符串}
注意:字符串需要用引号引起来
外面单引号,里面双引号
外面双引号,里面单引号
#{其他bean的id.引用的属性名}
#{此bean的id.方法名(参数)}
注意:
⑴ 即使是调用本bean的普通方法,也需要通过此bean的id来调用该方法
⑵ 如果参数是String类型,则参数需要引起来
#{T(全类名).常量}
#{T(全类名).静态方法(参数)}
调用静态方法
调用常量
①算术运算符:+、-、*、/、%、^
②字符串连接:+
③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
④逻辑运算符:and, or, not, |
⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
⑥正则表达式:matches
⑴ 导入jar包
除了:
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
还需要:
spring-aop-4.0.0.RELEASE.jar
注意:如果没有导入aop jar包,则运行时会报错
Caused by: java.lang.ClassNotFoundException: org.springframework.aop.TargetSource
⑵ 设置扫描包
需要添加context名称空间
xmlns:context="http://www.springframework.org/schema/context"
同时会在xsi:schemaLocation 中添加
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"
通过context的component-scan标签来设置自动扫描包
⑶ 用注解来声明bean对象
@Controller
public class UserView {}
@Service
public class UserService {}
@Repository
public class UserDao {}
@Controller 表现层
@Service 业务逻辑层
@Repository 数据访问层
@Component 组件
可以用在任何一个组建类上,一般用于不明确属于某个层的Bean对象的声明
Tips:分层注解可以给bean设置id属性值,如果不设置,则默认为bean的简单类名首字母小写
@Controller("userView")
public class UserAction {}
设置bean的id为userView
@Scope(value = “prototype”) 设置Bean对象的创建方式为原型【默认为singleton(单例)】
首先通过component-scan标签设置要扫描的包,同时还要设置use-default-filters属性为false
type:是根据annotation(注解),还是根据assignable(全类名)
expression:对应的全类名,如果type是注解,则全类名就是注解的全类名;否则就是要扫描的类的全类名
⑴ 根据注解扫描:
① 扫描所有带有Controller注解的类
② 扫描所有带有Service注解的类
③ 扫描所有带有Repository注解的类
④ 扫描所有带有Component注解的类
注意:通过注解过滤层次,是固定的写法,不能改变type和expression的值
⑵ 根据全类名扫描
type和expression 与 include的一样
@Autowired
通过此注解可以让IOC容器自动装配带有此注解的属性
注意:自动装配只能给自己定义的类型的属性赋值
@Qualifier
通过此注解,可以指定要专配的bean【根据id属性查找】
⑴ 根据所属类型查找
@Autowired
属性类型 属性名
要求:属性所属的类型的类只有一个
如果有多个,则报错:NoUniqueBeanDefinitionException
⑵ 根据属性名查找(根据bean的id查找)
@Autowired
属性类型 属性名
要求:属性名对应的类型只能有一个。即属性名和类型的注解的value值【即bean的id】相同的只能有一个
⑶ 根据Qualifier限定查找
@Autowired
@Qualifier(value = "bean的id")
属性类型 属性名
要求:Qualifier的value值和类型的注解的value值【即bean的id】相同的只能有一个
不能给定不存在的value值【bean的id】,否则报错:NoSuchBeanDefinitionException
⑷ 放弃装配
@Autowired(require = false)
@Qualifier(value = "")
属性类型 属性名
通过required=false放弃装配
⑴ 首先根据属性所属类型,查找对应的bean,如果有多个。可以改变属性类型为数组或集合
⑵ 如果只接收一个,则会根据属性名查找bean的id【注解的value值】
⑶ 如果还是多个,则根据Qualifier的value值查找bean的id
⑷ 如果还是有多个,则只能通过required = false 来放弃装配
根据类型 → 根据属性名 → Qualifier → (required = false)
注解可以用在成员变量上,也可以用在带参数的方法上,以及入参前
⑴ 用在带参数的方法上
属性类型 属性名;
@Autowired(required = false)
@Qualifier(value = "")
public void set属性方法(属性类型 属性名) {
this.属性名 = 属性名;
}
⑵ 用在方法入参前
属性类型 属性名;
@Autowired(required = false)
public void set属性方法(@Qualifier(value = "") 属性类型 属性名) {
this.属性名 = 属性名;
}
根据泛型来决定需要注入的bean对象
应用场景:当一个接口或抽象类被多个实现类实现或继承时,且需要调用接口或实现类的具体实现类或子类。则可以通过泛型的不同,来确定要注入的bean对象
根据泛型来指定要调用的具体实现类或子类,所以只要保证泛型类型的一致即可。虽然都写成String等类型也是能实现的,但是一般根据具体的bean来指定泛型
⑴ 在核心配置文件中,添加自动扫描包
⑵ 编写JavaBean
⑶ 分层编写带泛型的接口或抽象类
⑷ 分层编写具体的实现类或继承子类
⑸ 根据实现类或子类来调用具体的方法
【核心配置文件】
【JavaBean】
Animal和User
因为只是利用泛型,所以可以不必写具体的属性和方法
【Dao层】
带泛型的接口
public interface BaseDao {
void save();
}
实现类
@Repository
public class UserDao implements BaseDao {
@Override
public void save() {
System.out.println("插入用户记录");
}
}
@Repository
public class AnimalDao implements BaseDao {
@Override
public void save() {
System.out.println("插入动物记录");
}
}
【Service层】
带泛型的抽象基类
public abstract class BaseService {
@Autowired
protected BaseDao baseDao;
public abstract void save();
}
子类
@Service
public class UserService extends BaseService {
@Override
public void save() {
baseDao.save();
}
}
@Service
public class AnimalService extends BaseService {
@Override
public void save() {
baseDao.save();
}
}
【测试】
ApplicationContext ioc = new ClassPathXmlApplicationContext("???.xml");
UserService userService = (UserService) ioc.getBean("userService");
userService.save();
AnimalService animalService = (AnimalService) ioc.getBean("animalService");
animalService.save();
Tips:Service层和Dao层的泛型类型要一致,即都写成String也可以,但是一般不会这样做