IOC
即Inversion of Control,控制反轉(zhuǎn),也叫依賴注入。
不使用Spring時(shí),創(chuàng)建Service實(shí)例的主動(dòng)權(quán)在Controller,即自己主動(dòng)去new一個(gè)出來馬上就可以使用。
使用Spring后,Controller不能主動(dòng)去new Service類的實(shí)例,只有Spring才能夠new Service類的實(shí)例,Action只能等Spring創(chuàng)建好實(shí)例后,將實(shí)例分配給它之后,才能夠使用。
減少Bean的耦合度,實(shí)現(xiàn)Bean的可插拔
如下圖代碼,Dao層有兩個(gè)UserDao的實(shí)現(xiàn)類,當(dāng)需要從一個(gè)切換到另一個(gè)時(shí),僅需把舊的實(shí)現(xiàn)類的@Repository注解注釋掉,把新的實(shí)現(xiàn)類加上@Repository注解即可,Service層與Controller層的代碼無需改動(dòng)。
另:一般僅對(duì)可復(fù)用的邏輯類,即單例的類,加入注解,進(jìn)行IOC管理,如Dao實(shí)現(xiàn)類,Service實(shí)現(xiàn)類,Controller類。
/* Dao層 */
//@Repository
public class UserDaoJdbcImpl implements UserDao {...}
@Repository
public class UserDaoHibernateImpl implements UserDao {...}
/* Service層 */
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public User getUser(int id) {
return userDao.findById(id);
}
}
/* Controller層 */
@Controller
@RequestMapping(path = "/user")
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(path = "/detail/{id}", method = RequestMethod.GET)
@ResponseBody
public User getUser(@PathVariable("id") int id) {
return userService.getUser(id);
}
}
控制Bean生命周期及作用域
Bean: 有Spring容器管理的Java 對(duì)象
使用@PostConstruct注解相關(guān)方法,可在Bean初始化時(shí)調(diào)用該方法。
使用@PreDestroy注解相關(guān)方法,可現(xiàn)在Bean銷毀前調(diào)用該方法。
使用@Scope注解可控制Bean的作用域,默認(rèn)為單例模式,若需要多個(gè)實(shí)例,可使用prototype(盡管一般很少使用)。
@Service
//@Scope("prototype")
public class UserServiceImpl implements UserService {
@PostConstruct
public void init() {
System.out.println("init UserServiceImpl");
}
@PreDestroy
public void destroy() {
System.out.println("destroy UserServiceImpl");
}
}
Containers
BeanFactory & ApplicationContext
The Spring Framework comes with two IOC containers – BeanFactory and ApplicationContext. The BeanFactory is the most basic version of IOC containers, and the ApplicationContext extends the features of BeanFactory.
BeanFactory loads beans on-demand. The beans defined in our BeanFactory will be loaded only when we explicitly call the getBean() method. While ApplicationContext loads all beans at startup.
Thus, BeanFactory is lightweight as compared to ApplicationContext. It's generally recommended to use the ApplicationContext, and we should use BeanFactory only when memory consumption is critical.
Resource res = new ClassPathResource("ioc-container-difference-example.xml");
BeanFactory factory = new XmlBeanFactory(res);
// 此時(shí)BeanFactory加載Bean
Student student = (Student) factory.getBean("student");
// 此時(shí)ApplicationContext加載Bean
ApplicationContext context =
new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
ApplicationContext核心功能:獲取Bean
// 1 實(shí)現(xiàn)ApplicationContextAware接口
class ExampleApplicationTests implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 2 重寫setApplicationContext方法,獲取ApplicationContext容器
this.applicationContext = applicationContext;
}
// 3 使用ApplicationContext獲取Bean
public void testApplicationContext() {
// 通過類型獲取
UserDao dao1 = applicationContext.getBean(UserDao.class);
// 通過名字獲取
UserDao dao2 = (UserDao) applicationContext.getBean("userDaoHibernateImpl");
// 通過類型和名字獲?。ó?dāng)一個(gè)抽象類有多個(gè)實(shí)現(xiàn)類時(shí))
UserDao dao3 = applicationContext.getBean("userDaoHibernateImpl", UserDao.class);
}
}
AOP
即Aspect Oriented Programming, 面向切面編程

JoinPoint:連接點(diǎn),即目標(biāo)類中所有可能被切入的方法。
PointCut:切入點(diǎn),即目標(biāo)類中實(shí)際被切入的方法。
如:一個(gè)類中有方法1,方法2, 方法3,則這三個(gè)方法都是連接點(diǎn),如果選擇方法1進(jìn)行切入,則方法1就為切入點(diǎn)。
Advice:通知,即用于規(guī)定何時(shí)對(duì)切入點(diǎn)進(jìn)行何種操作。
Aspect:切面,包含了PointCut和Advice的類。
織入:將Advice中的操作應(yīng)用與切點(diǎn)的過程。
如果目標(biāo)類實(shí)現(xiàn)了接口,采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP,原理是Method方法反射
如果目標(biāo)類沒有實(shí)現(xiàn)接口,采用用CGLIB實(shí)現(xiàn)AOP,原理是對(duì)代理的目標(biāo)類生成一個(gè)子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)。(由于是繼承目標(biāo)類的方式,故無法對(duì)static、private、final修飾的方法進(jìn)行代理)
JDK動(dòng)態(tài)代理要比cglib代理執(zhí)行速度快,但性能不如cglib好。
@Component
@Aspect
public class LogAspect {
// 說明切入點(diǎn)為返回值任意的 com.nowcoder.example.service包下的所有類的所有方法
@Pointcut("execution(* com.nowcoder.example.service.*.*(..))")
private void serviceAccess() { }
@Before("serviceAccess()")
public void before() {
System.out.println("log before");
}
@AfterThrowing("serviceAccess()")
public void afterThrowing() {
System.out.println("log afterThrowing");
}
@AfterReturning("serviceAccess()")
public void afterReturning() {
System.out.println("log afterReturning");
}
@After("serviceAccess()")
public void after() {
System.out.println("log after");
}
// Around方式功能強(qiáng),但代碼出錯(cuò)時(shí),影響范圍也大,一般不使用
@Around("serviceAccess()")
public Object log(ProceedingJoinPoint joinPoint) {
Object obj = null;
try {
System.out.println("log before");
// 調(diào)用切入點(diǎn)的方法
obj = joinPoint.proceed();
System.out.println("log afterReturning");
} catch (Throwable throwable) {
System.out.println("log afterThrowing");
} finally {
System.out.println("log after");
}
return obj;
}
}