Spring IOC & Containers & AOP

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 containersBeanFactory 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, 面向切面編程

AOP.png

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;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容