欲实现路由验证,写了一个Hibernate
拦截器,在对数据库进行操做以前对菜单进行校验,若是存在,则抛出异常,终止保存操做的执行;若是不存在,则继续执行。拦截器代码以下:html
package com.mengyunzhi.measurement.interceptor; import com.mengyunzhi.measurement.Service.WebAppMenuService; import com.mengyunzhi.measurement.entity.WebAppMenu; import org.hibernate.EmptyInterceptor; import org.hibernate.type.Type; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import java.io.Serializable; /** * 继承自Hibernate提供的EmptyInterceptor拦截器 * 在进行数据库操做以前进行处理,判断该路由是否惟一 * 抽象菜单:则不能相同 * 其子菜单:若是不属于同一父菜单,能够相同 */ public class WebAppMenuInterceptor extends EmptyInterceptor { @Autowired private WebAppMenuService webAppMenuService; /** * 重写OnSave方法 */ @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { // 若是是菜单实体 if (entity instanceof WebAppMenu) { // 则执行路由验证 this.validateWebAppMenuRoute((WebAppMenu) entity); } // 调用父类的onSave方法,不太明白这个方法设计返回boolean值是为什么? return super.onSave(entity, id, state, propertyNames, types); } /** * 路由验证 */ private void validateWebAppMenuRoute(WebAppMenu webAppMenu) { // 初始化变量 Boolean isAbstract = webAppMenu.isAbstract(); String routeName = webAppMenu.getRouteName(); Boolean isExist; // 若是是抽象菜单 if (isAbstract){ // 根据路由名和抽象为true,判断是否存在 isExist = webAppMenuService.existsByRouteNameAndIsAbstractIsTrue(routeName); } else { // 非抽象,判断其同一父菜单下是否有相同菜单 isExist = webAppMenuService.existsByRouteNameAndParentWebAppMenu(routeName, (WebAppMenu) webAppMenu.getParentWebAppMenu()); } if (isExist) { throw new DataIntegrityViolationException("该路由非法"); } } }
可是测试一直通不过,报错:没法加载上下文。java
打印了一下webAppMenuService
的值,发现全都是null
;果真,这个东西没注入进来。web
看来@Autowired
并非万能的,才有了下文对Spring IOC
的学习。spring
系统的学习一下,不要像之前同样去StackOverflow
查答案,而后照搬别人的解决方案,感受如今对Spring
的了解仍是太少了。数据库
IOC
:控制反转,你们都熟知的定义这里就再也不赘述了。segmentfault
其实,Spring
负责帮咱们管理对象,就像我在关于接口的代码复用中实现的手动注入对象的代码同样。架构
/** * 鸟 */ public class Bird extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } /** * 蝙蝠 */ public class Bat extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } public class Main { public static void main(String[] args) { Volitant volitant = new VolitantImpl(); Bird bird = new Bird(); bird.setVolitant(volitant); bird.fly(); Bat bat = new Bat(); bat.setVolitant(volitant); bat.fly(); } }
上述代码是手动对一个类中须要设置的对象进行注入,其实Spring IOC
就是一个帮助咱们管理和注入对象的角色。maven
说到对象的管理,不得不提一个“容器”的概念,这个容器装的是对象。ide
固然,这里的容器是咱们的想象的一个概念,它的术语名称你们可能会很熟悉——“上下文”,只是上下文是一种更高级的容器罢了。学习
创建一个maven
项目,依赖引入spring-context
,咱们以一个实际的Spring
使用来学习IOC
。
这是Spring
的七个模块,在IBM
的开发者学习文档中找到的。大体看了一下介绍,core
是Spring
的核心库,有各类功能,context
调用核心库中的方法,对外提供容器以及其余功能。
BeanFactory
是一个最基础的容器接口,提供了从容器中获取对象,判断容器中是否有某对象的方法。只有最基础的容器功能,如今已经不建议使用。
public interface BeanFactory { String FACTORY_BEAN_PREFIX = "&"; Object getBean(String var1) throws BeansException; <T> T getBean(String var1, @Nullable Class<T> var2) throws BeansException; Object getBean(String var1, Object... var2) throws BeansException; <T> T getBean(Class<T> var1) throws BeansException; <T> T getBean(Class<T> var1, Object... var2) throws BeansException; boolean containsBean(String var1); boolean isSingleton(String var1) throws NoSuchBeanDefinitionException; boolean isPrototype(String var1) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String var1, @Nullable Class<?> var2) throws NoSuchBeanDefinitionException; @Nullable Class<?> getType(String var1) throws NoSuchBeanDefinitionException; String[] getAliases(String var1); }
既然咱们要从容器中拿对象?那咱们就须要告诉IOC
容器,你的容器中应该有什么对象,而且各个对象之间的依赖关系,总不能我写的每一个类都实例化到容器中吧?因此诞生了xml
配置文件。相似下面这样。
一个bean
,id
是什么,对应的是哪一个类,而且这个对象依赖于其余的什么对象。
看了一下,有个删除线,不建议使用,也就没有过深的研究,咱们投入上下文的怀抱。
ApplicationContext
固然也支持xml
配置,其实现类为ClassPathXmlApplicationContext
。
由于本人更喜欢注解,这里深刻学习一下注解的配置方式。
模拟项目中的真实架构,Service
接口,依赖,为其注入实现。
TestService
接口:
package com.mengyunzhi.spring.service; public interface TestService { }
TestService
实现:
package com.mengyunzhi.spring.service; import org.springframework.stereotype.Component; @Component public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的实现"; } }
你问这里为何加的是@Component
注解而不是@Service
,其实这几个注解,不管是@Service
、@Repository
、@Controller
实际上都是声明一个组件,让其被Spring
所管理,这么设计,不过是让人们看代码上的注解时,就能清晰地了解这个类的职责。
注意,这里加的@Component
告诉Spring
,你为我管理这个类。而后还有一个注解,@ComponentScan
就是表示我要扫描哪一个包下的有@Component
注解的类,并对其进行管理。
主方法:
package com.mengyunzhi.spring; import com.mengyunzhi.spring.service.TestService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); TestService testService = context.getBean(TestService.class); System.out.println(testService); } }
新建一个基于注解的上下文,而后获取TestService
这个类的实例,同时打印。注意,这里的@ComponentScan
必定要加,不加会报错。(我感受这种方式比xml
配置简单多了,@
几下就完成了配置)
运行结果:
突发奇想,咱们修改一下toString
方法的代码,相信你看到代码就知道我想干什么了。
package com.mengyunzhi.spring.service; import org.springframework.stereotype.Component; @Component public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的实现\n" + "个人内存地址是:" + super.toString(); } }
修改主方法代码,如今咱们从上下文中获取两个TestService
的实例。
package com.mengyunzhi.spring; import com.mengyunzhi.spring.service.TestService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); TestService testService1 = context.getBean(TestService.class); System.out.println(testService1); TestService testService2 = context.getBean(TestService.class); System.out.println(testService2); } }
咱们发现这两个对象的内存地址是相同的,说明这两个对象引用的是同一块内存。
这就涉及到Bean
对象的Scope
属性,默认的Scope
属性是Singleton
,单例,全容器共享这个实例。
若是想使用多例,就将其Scope
属性设置为Prototype
。
@Component @Scope("prototype") public class TestServiceImpl implements TestService { @Override public String toString() { return "我是TestServiceImpl, TestService的实现\n" + "个人内存地址是:" + super.toString(); } }
在TestServiceImpl
上加上@Scope
注解,注意这里的prototype
必定要是小写的。
再次运行:
两个内存地址不一样,这是两个独立的对象。
咱们本身写的类,咱们想将其加入到容器中,咱们能够将其加上@Component
注解将其添加到容器中,可是若是咱们想把某个第三方库中的对象添加到容器中怎么办呢?咱们可不能去改动依赖开源库的代码。
package com.mengyunzhi.spring; public class OtherService { public void show() { System.out.println("我是一个第三方的库"); } }
package com.mengyunzhi.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("com.mengyunzhi.spring") public class Application { public static void main(String []args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); OtherService otherService = context.getBean(OtherService.class); otherService.show(); } @Bean OtherService getOtherService() { return new OtherService(); } }
@Configuration
:表示这是一个Spring
的配置类,而后就能够在其中配置相关Bean
,经过@Bean
注解,会将其标注的方法的返回值对象加入到容器中。
运行结果:
Spring IOC
其实就是一个容器,咱们常说的启动一个Spring
项目其实就是所谓的加载容器(上下文)。
路漫漫其修远兮,让咱们一块儿探索
Spring
的本质!