IOC是什么
IOC:控制反转, 是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
//传统方式
public class Demo{
// 手动创建对象 new
User user = new User();
void test(){
user.speak();
}
}
// IOC
public class Demo{
// User通过容器来进行管理,注入
@Autowired
User user;
void test(){
user.speak();
}
}
为什么使用IOC
日常开发过程中推荐面向抽象编程,面向抽象变成会产生类的依赖,这是我们就需要一个管理的容器来管理这些依赖,同样,类的产生也就交给了容器。
实际使用场景:
事务使用:假如一个方法需要进行事务管理,那么我们正常操作是自己手动进行事务管理,但这样操作的缺点是。
- 假如说这个方法,有的方法调用需要开启事务,而有的方法不需要开启,则无法控制。
- 违反单一指责,将事务参杂在业务逻辑中
public class OrderService {
public void placeOrder(){
// 开启事务
// 操作过程
// 关闭事务
}
}
如果使用容器管理,即可通过容器来进行管理事务。
@Service
public class OrderService {
public void placeOrder(){
// 操作过程
}
}
public class Commodity{
@Autowired
OrderService service;
@Transaction
public void buyComm(){
service.placeOrder();
}
}
依赖注入的方式
两种注入方式:
- 构造方法注入 Constructor-based Dependency Injection
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
<beans>
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg ref="beanTwo"/>
<constructor-arg ref="beanThree"/>
</bean>
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
</beans>
- set方法注入 Setter-based Dependency Injection
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
自动装配
自动装配的4种方式
- no
- bytype
- byname
- constructor
Spring懒加载
懒加载:顾名思义,项目启动时并未初始化,而是使用时才初始化。
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
@Lazy
@Service
public class ExampleBean {
}
Bean的作用域
问题:当Singleton中引用prototype的bean时,会引发什么问题?
答:prototype会失去意义,在Singleton单例的对象中,引入的prototype只会初始化一次
@Scope("singleton")
@Repository
public class SingletonDao {
@Autowired
PrototypeDao prototypeDao;
public PrototypeDao getPrototypeDao() {
return prototypeDao;
}
}
@Scope("prototype")
@Repository
public class PrototypeDao {
}
@Configuration
@ComponentScan("mine.test")
public class App {
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(App.class);
for (int i = 0; i < 3; i++) {
SingletonDao dao = context.getBean(SingletonDao.class);
System.out.println("SingletonDao:"+dao.hashCode());
System.out.println("PrototypeDao:"+dao.getPrototypeDao().hashCode());
}
}
}
/* 结果:可见原型未起作用
SingletonDao:1849433705
PrototypeDao:411631404
SingletonDao:1849433705
PrototypeDao:411631404
SingletonDao:1849433705
PrototypeDao:411631404
*/
如何解决此问题?官网给出两种答案。
- 实现ApplicationContextAware
- 使用LookUp
// 方法一:实现ApplicationContextAware
@Scope("singleton")
@Repository
public class SingletonDao implements ApplicationContextAware {
private ApplicationContext applicationContext;
public PrototypeDao getPrototypeDao() {
return applicationContext.getBean(PrototypeDao.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// 方法二: LookUp
@Scope("singleton")
@Repository
public abstract class SingletonDao {
@Lookup
public abstract PrototypeDao getPrototypeDao();
}
SpringBean生命周期的回调
SpringBean的回调方式
- 实现
InitializingBean
&DisposableBean
@PostConstruct
&@PreDestroy
注解
@Service //方式一:实现接口
public class AnotherExampleBean implements InitializingBean,DisposableBean {
AnotherExampleBean(){
// Construction method
}
@Override
public void afterPropertiesSet() {
// do some initialization work
}
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
@Service //方式二:注解方式
public class AnotherExampleBean {
AnotherExampleBean(){
// Construction method
}
@PostConstruct`
public void init() {
// do some initialization work
}
@PreDestroy
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
课外知识
Spring编程风格有三种
- annotation-base (注解方式)@AspectJ support
- Schema-based App (XML方式)
- Java-base
@Autowired和@Resource的区别?
-
@Autowired使用byType的方式,如果未找到对应类型,采用byName(属性名,非set方法)注入
-
@Resource默认采用byName(属性名,非set方法)注入,如果未找到对应名称,则去找byType的方式
@Component、@Repository、@Service和@Controller的区别?
- @Component是任何Spring管理组件的通用原型
- 相当于类型的区分
- 未来可能重大更新,建议按照对应的规则使用
如何解决注入找到多个Bean对象的问题?
- @Primary
- @Qualifier
// 定义接口
public interface MovieCatalog {
}
// 方式一:多实现使用@Primary
@Configuration
public class MovieConfiguration {
@Primary
@Bean("main")
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
}
// 方式二:注入时使用@Qualifier
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
}
如何在获取Bean对象?
- 实现ApplicationContextAware
@Scope("singleton")
@Repository
public class SingletonDao implements ApplicationContextAware {
private ApplicationContext applicationContext;
public PrototypeDao getPrototypeDao() {
return applicationContext.getBean(PrototypeDao.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Java中循环引用,GC如何回收?
待补充…
Spring如何解决循环引用问题?
简单来说,先有一层缓存,将bean存起来后,再进行相互引用。
解析,待补充…
官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans