IOC
即Inversion of Control,控制反转,也叫依赖注入。
不使用Spring时,创建Service实例的主动权在Controller,即自己主动去new一个出来马上就可以使用。
使用Spring后,Controller不能主动去new Service类的实例,只有Spring才能够new Service类的实例,Action只能等Spring创建好实例后,将实例分配给它之后,才能够使用。
减少Bean的耦合度,实现Bean的可插拔
如下图代码,Dao层有两个UserDao的实现类,当需要从一个切换到另一个时,仅需把旧的实现类的@Repository注解注释掉,把新的实现类加上@Repository注解即可,Service层与Controller层的代码无需改动。
另:一般仅对可复用的逻辑类,即单例的类,加入注解,进行IOC管理,如Dao实现类,Service实现类,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 对象
使用@PostConstruct注解相关方法,可在Bean初始化时调用该方法。
使用@PreDestroy注解相关方法,可现在Bean销毁前调用该方法。
使用@Scope注解可控制Bean的作用域,默认为单例模式,若需要多个实例,可使用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);
// 此时BeanFactory加载Bean
Student student = (Student) factory.getBean("student");
// 此时ApplicationContext加载Bean
ApplicationContext context =
new ClassPathXmlApplicationContext("ioc-container-difference-example.xml");
ApplicationContext核心功能:获取Bean
// 1 实现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");
// 通过类型和名字获取(当一个抽象类有多个实现类时)
UserDao dao3 = applicationContext.getBean("userDaoHibernateImpl", UserDao.class);
}
}
AOP
即Aspect Oriented Programming, 面向切面编程
JoinPoint:连接点,即目标类中所有可能被切入的方法。
PointCut:切入点,即目标类中实际被切入的方法。
如:一个类中有方法1,方法2, 方法3,则这三个方法都是连接点,如果选择方法1进行切入,则方法1就为切入点。
Advice:通知,即用于规定何时对切入点进行何种操作。
Aspect:切面,包含了PointCut和Advice的类。
织入:将Advice中的操作应用与切点的过程。
如果目标类实现了接口,采用JDK的动态代理实现AOP,原理是Method方法反射
如果目标类没有实现接口,采用用CGLIB实现AOP,原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强。(由于是继承目标类的方式,故无法对static、private、final修饰的方法进行代理)
JDK动态代理要比cglib代理执行速度快,但性能不如cglib好。
@Component
@Aspect
public class LogAspect {
// 说明切入点为返回值任意的 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方式功能强,但代码出错时,影响范围也大,一般不使用
@Around("serviceAccess()")
public Object log(ProceedingJoinPoint joinPoint) {
Object obj = null;
try {
System.out.println("log before");
// 调用切入点的方法
obj = joinPoint.proceed();
System.out.println("log afterReturning");
} catch (Throwable throwable) {
System.out.println("log afterThrowing");
} finally {
System.out.println("log after");
}
return obj;
}
}