@Configuration: 用于类上,告诉Spring这是一个配置类
@ComponentScan() 包扫描
@ComponentScans()可以配置多个包扫描(java8之后允许重复注解,而@ComponentSan就属于重复注解,可以直接重复使用,不再依赖@ComponentScans())
用于扫描@Component/@Service/@Controller/@Repository标记的组件
value:包的路径
excludeFilters:指定扫描的时候按照什么规则排除哪些组件
includeFilters:指定扫描的时候只需包含哪些组件
@Filter()
type: 有5中类型:
1,FilterType.ANNOTATION(按照注解),此时classes属性对应的是组件注解的类
2,FilterType.ASSIGNABLE_TYPE,classes属性可以写自己所建的类
3,FilterType.ASPECTJ:使用ASPECTJ表达式
4,FilterType.REGEX:使用正则表达式
5,FilterType.CUSTOM ,classes属性为自己写的类过滤器类(TypeFilter的实现类)
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
classes:对应的类;
useDefaultFilters:
使用默认扫描,默认值是true,扫描包下所有的组件 ,当使用include是应设置为false。
在@Configuration标注的配置类上使用,声明容器组件,相当于xml文件中的
告诉Spring这是一个配置类
表示该类是一个组件
表示该类是一个Service组件
表示该类是一个Controller组件
表示该类是一个数据持久组件
一般配置在配置类上,表示在一定条件下该配置类生效;value属性是Condition的子类,也可应用在bean上
//判断是否windows系统
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if(property.contains("Windows")){
return true;
}
return false;
}
}
标注在组件上,例如@Bean标注的方法上,当要注入的组件类型有多个组件时,被@Primary标注的组件会优先被租入(@Autowired注入是依据类注入的)。
标注在组件上,设置组件懒加载,属性value默认是true,即加上注解后该组件就成为了懒加载的组件,懒加载组件在容器初始化时不进行创建,当获取时进行创建(单实例bean默认在容器启动的时候创建对象);
调整作用域:默认的bean时单实例的:
scopeName属性(value)的值有:
prototype:多实例,ioc容器启动并不会去调用方法创建对象放在容器中。每次在获取的时候才会调用方法创建对象
singleton:单实例(默认值):ioc容器启动会调用方法创建对象放在ioc容器中。以后每次获取就是直接从容器(map.get())中拿;
request:同一次请求创建一个实例;
session:同一个session常见一个实例;
导入组件,组件的id默认是组件的全类名
value属性是Class<?>[] 可以导入的类有1,普通的组件类2,ImportSelector的实现类,3ImportBeanDefinitionRegistrar的实现类
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//importingClassMetadata
//方法不要返回null值
return new String[]{"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;调用
* BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
if(definition && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
使用Spring提供的FactoryBean(工厂Bean)
1),默认获取到的是工厂bean调用的是getObject创建的对象(Color)
2)、要获取工厂Bean本身,我们需要给id前面加一个&,例如&colorFactoryBean
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class> getObjectType() {
// TODO Auto-generated method stub
return Color.class;
}
//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
-----------------------------------------------------------------------------
在配置类中
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
bean的生命周期:bean的创建--初始化---销毁
容器管理bean的声明周期
我们可以自定义初始化和销毁方法;容器在bean进行到生命周期的时候回来调用我们自定义的初始化和销毁方法;
三种方式:
1,指定初始化和销毁的方法
通过@Bean指定init-method和destory-method
2,使用jsr250的注解:
@PostConstruct:在bean创建完成并进行赋值后来调用
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
3,通过让bean实现InitializingBean(实现初始化逻辑)和DisposableBean(定义销毁逻辑)
4,使用bean的后置处理器BeanPostProcessor(接口)
在bean初始化前后进行一些处理工作
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
源码顺序:
populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
initializeBean
{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
使用@value赋值:
1,基本数据类型
2,可以写SPEL表达式#{}
3,如果配置类引入外部文件可以用${}取出配置文件[properties]中的值(在运行环境变量里面的值(配置文件被引入配置类后,在运行过程中所有的内容会存入环境变量中,环境变量中海有其他的信息,例如os.name))
1)@Autowired:自动注入
1>默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class)找到了就赋值;
2>如果找到了多个相同类型的组件,再将属性的名称作为组件的id去容器中查找:applicationContext.getBean("bookDao");
3>@Qualifier("bookDao"),结合@Autowired使用,指定装配组件的id
4>自动装配默认一定要将属性赋值好,没有就会报错,如果需要自动装配失败时不报错,则使用@Autowired(required=false)
5>@Primary:让Spring进行自动装配的时候,默认使用首选的bean
2)Spring还支持使用@Resource(JSR250)和@Inject(JSR330)
@Resource:
可以和@Autowired一样实现自动装配功能,默认时按照组件名称进行装配的;没有能支持@Primary功能,没有支持@Autowired(required=false)的功能;
@Inject:
需要导入javax.inject的jar,和@Autowired的功能一样,没有required=false的功能;
@Autowired:Spring定义的;@Resource、@Inject都是java规范;
3)@Autowired使用在构造器,参数,方法,属性上,都时从容器中获取参数组件的值
1>@Bean 的方法的参数默认从容器中获取;默认不写@Autowired效果是一样的,都能自动装配;
2>如果组件只有一个有参构造器,这个构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取的
使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}可以取出配置文件的值
@PropertySource("classpath:/dbconfig.properties")
用于配置多个@PropertySource,在java8后可以不使用。
Spring为我们提供的可以根据当前环境,动态地激活和切换一系列组件的功能;
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定时任何环境都能注册这个组件;
1,加了@Profile的bean,只有在这个环境被激活的时候才能注册到容器中。默认时default环境;
2,写在配置类上,只有时指定的环境的时候,整个配置类里面的所有配置才能生效
3,没有标注环境标识的bean,在任何环境下都时加载的。
@Autowired标注在方法或参数上,容器在创建对象时会调用该方法,该方法参数为容器中的组件
@Bean标注的方法的参数上,可以省略@Autowired,默认从容器中找
@Autowired标注在构造器上,容器在攒关键对象时会用这个构造器,参数为容器中的组件
若类只有一个有参构造,则@Autowired可以省略,参数为容器中的组件
当组件实现ApplicationContextAware时,可以把ApplicationContext容器注入到组件中;当然也可以直接用@Autowired直接注入
自定义组件想要使用Spring容器底层的一些组件(如:ApplicationContext,BeanFactory,xxx)
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件把Spring容器底层一些组件注入到自定义的Bean中;
@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
System.out.println("传入的ioc:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:"+resolveStringValue);
}
}
指在程序运行期间动态地将某段代码切入到指定方法指定位置进行运行的编码方式
开启基于注解的aop模式
告诉Spring哪个类时切面类
前置通知(@Before):在目标方法运行之前运行;
后置通知(@After):在目标方法运行结束之后运行(无论方法正常结束还是异常结束);
返回通知(@AfterReturning):在目标方法正常返回之后运行
异常通知(@AfterThrowing):在目标方法出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
切入点:用于抽取公共地切入点表达式
本类使用时直接写方法名
其他类使用需要写类路径和方法名
三步:
/**
* 切面类
* @author lfy
*
* @Aspect: 告诉Spring当前类是一个切面类
*
*/
@Aspect
public class LogAspects {
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
public void pointCut(){};
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
}
@After("com.atguigu.aop.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
}
//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}
}
特别注意:一定要配置plateformTransactionManager
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
开启基于注解地事务管理功能
表示当前方法是一个事务方法
beanFactory的后置处理器
在BeanFactory标准初始化之后调用,来定制和修改BeanFactory的内容
此时所有的bean定义已经加载到Beanfactory,但是Bean实例还没有创建
是BeanFactoryPostProcessor的子类
在所有bean定义信息竟要被加载时调用
优先于BeanFactoryPostProcessor执行;
利用BeanDefinitionRegistryPostProcessor给容器中在额外添加一些组件
@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:"+beanFactory.getBeanDefinitionCount());
}
//BeanDefinitionRegistry Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("postProcessBeanDefinitionRegistry...bean的数量:"+registry.getBeanDefinitionCount());
//RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition();
registry.registerBeanDefinition("hello", beanDefinition);
}
}
监听容器发布的事件,事件驱动模型开发:
类为 public interface ApplicationListener
使用步骤:
1,写一个监听器(ApplicationListener实现类)来监听某个时间(ApplicationEvent及子类),并注册到容器中
@Component
public class MyApplicationListener implements ApplicationListener {
//当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:"+event);
}
}
或者用普通的组件在一个特定的方法上(方法的参数是ApplicationEvent event)标注@EventListener(classes={ApplicationEvent.class})(所说的ApplicationEvent都是本身及其子类)
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
}
2,只要容器中有相关时间的发布,我们就能监听到这个事件
Spring中也定义了很多事件,如:
ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
ContextClosedEvent:关闭容器会发布这个事件;
3,发布一个事件:
applicationContext.publishEvent(event)
servlet3.0规定容器启动时会META-INF/services/javax.servlet.ServletContainerInitializer文件中的ServletContainerInitialzer类的实现的类路径,该实现所标注的@HandlesTypes中的类会传递进来
里边的内容是 com.atguigu.servlet.MyServletContainerInitializer
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* 应用启动的时候,会运行onStartup方法;
*
* Set> arg0:感兴趣的类型的所有子类型;
* ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
*
* 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
* 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
* 必须在项目启动的时候来添加;
* 1)、ServletContainerInitializer得到的ServletContext;
* 2)、ServletContextListener得到的ServletContext;
*/
@Override
public void onStartup(Set> arg0, ServletContext sc) throws ServletException {
// TODO Auto-generated method stub
System.out.println("感兴趣的类型:");
for (Class> claz : arg0) {
System.out.println(claz);
}
//注册组件 ServletRegistration
ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
//配置servlet的映射信息
servlet.addMapping("/user");
//注册Listener
sc.addListener(UserListener.class);
//注册Filter FilterRegistration
FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
//配置Filter的映射信息
filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
}
}
/**
* 监听项目的启动和停止
* @author lfy
*
*/
public class UserListener implements ServletContextListener {
//监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent arg0) {
// TODO Auto-generated method stub
System.out.println("UserListener...contextDestroyed...");
}
//监听ServletContext启动初始化
@Override
public void contextInitialized(ServletContextEvent arg0) {
// TODO Auto-generated method stub
ServletContext servletContext = arg0.getServletContext();
System.out.println("UserListener...contextInitialized...");
}
}
@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、支持异步处理asyncSupported=true
//2、开启异步模式
System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
AsyncContext startAsync = req.startAsync();
//3、业务逻辑进行异步处理;开始异步处理
startAsync.start(new Runnable() {
@Override
public void run() {
try {
System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
sayHello();
startAsync.complete();
//获取到异步上下文
AsyncContext asyncContext = req.getAsyncContext();
//4、获取响应
ServletResponse response = asyncContext.getResponse();
response.getWriter().write("hello async...");
System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
} catch (Exception e) {
}
}
});
System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
}
public void sayHello() throws Exception{
System.out.println(Thread.currentThread()+" processing...");
Thread.sleep(3000);
}
}
由于Springmvc的web包下METE-INF已经配置了servlet3.0的配置只需要包@HandleTypes里边的类实现就行了,基于注解的配置一般实现AbstractAnnotationConfigDispatcherServletInitializer,他是HandleTypes类的子实现;
//web容器启动的时候创建对象;调用方法来初始化容器以前前端控制器
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//获取根容器的配置类;(Spring的配置文件) 父容器;
@Override
protected Class>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class>[]{RootConfig.class};
}
//获取web容器的配置类(SpringMVC配置文件) 子容器;
@Override
protected Class>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class>[]{AppConfig.class};
}
//获取DispatcherServlet的映射信息
// /:拦截所有请求(包括静态资源(xx.js,xx.png)),但是不包括*.jsp;
// /*:拦截所有请求;连*.jsp页面都拦截;jsp页面是tomcat的jsp引擎解析的;
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[]{"/"};
}
}
就相当于载入连个配置文件;
其中mvc的配置:
//SpringMVC只扫描Controller;子容器
//useDefaultFilters=false 禁用默认的过滤规则;
@ComponentScan(value="com.atguigu",includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class})
},useDefaultFilters=false)
@EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
//定制
//视图解析器
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
// TODO Auto-generated method stub
//默认所有的页面都从 /WEB-INF/ xxx .jsp
//registry.jsp();
registry.jsp("/WEB-INF/views/", ".jsp");
}
//静态资源访问
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// TODO Auto-generated method stub
configurer.enable();
}
//拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO Auto-generated method stub
//super.addInterceptors(registry);
registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/**");
}
}
public class MyFirstInterceptor implements HandlerInterceptor {
//目标方法运行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO Auto-generated method stub
System.out.println("preHandle..."+request.getRequestURI());
return true;
}
//目标方法执行正确以后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("postHandle...");
}
//页面响应以后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("afterCompletion...");
}
}
@ResponseBody
@RequestMapping("/async01")
public Callable async01(){
System.out.println("主线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
Callable callable = new Callable() {
@Override
public String call() throws Exception {
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("副线程开始..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return "Callable async01()";
}
};
System.out.println("主线程结束..."+Thread.currentThread()+"==>"+System.currentTimeMillis());
return callable;
}
1、控制器返回Callable
2、Spring异步处理,将Callable 提交到 TaskExecutor 使用一个隔离的线程进行执行
3、DispatcherServlet和所有的Filter退出web容器的线程,但是response 保持打开状态;
4、Callable返回结果,SpringMVC将请求重新派发给容器,恢复之前的处理;
5、根据Callable返回的结果。SpringMVC继续进行视图渲染流程等(从收请求-视图渲染)
@ResponseBody
@RequestMapping("/createOrder")
public DeferredResult
@ResponseBody
@RequestMapping("/create")
public String create(){
//创建订单
String order = UUID.randomUUID().toString();
DeferredResult deferredResult = DeferredResultQueue.get();
deferredResult.setResult(order);
return "success===>"+order;
}
当在3秒内完成时,上边异步的完成,否则超时异常,此段代码时模仿jmx实现
public class DeferredResultQueue {
private static Queue> queue = new ConcurrentLinkedQueue>();
public static void save(DeferredResult deferredResult){
queue.add(deferredResult);
}
public static DeferredResult get( ){
return queue.poll();
}
}