springmvc基础-注解方式编程和非注解方式编程

一、简单情境下

1、配置文件概述

2、注册转发器

在web.xml文件中,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
	
	<!-- 配置 DispatcherServlet -->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
		<!-- 
			实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
			默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
		-->
		<!--  
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		-->
		<load-on-startup>1</load-on-startup>
        <!-- load on starup 大于、等于0代表随系统启动后加载 ,数字越小优先级越高 ; 数值为负数时,启动时不加载,调用时再加载 -->
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
     <!-- 
              第一种:*.action 或者 *.do,访问以.action或*.do结尾 由DispatcherServlet进行解析
              第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析
              使用此种方式可以实现 RESTful风格的url
              第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
              仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错。
       -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

<!-- 下面这个没有写不要紧,因为tomcat服务器默认 index为首页 --> 
<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

3、配置转发器

转发配置文件–>【前面web.xml配置的是在src下, 取名springmvc.xml文件。如果是默认配置应该在WEB-INF下即和web.xml同目录,取名dispatcherServlet-servlet.xml文件】,其中 配置自定扫描的包 ,是为了扫描指定包文件中,有@Controller ,@Component等注解的类文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	
    <!-- 对于注解的Handler可以单个配置 实际开发中建议使用组件扫描 -->
    <!-- <bean class="com.test.springmvc.handlers.HelloWorld" />  -->
    <!-- 可以扫描controller、service、...这里让扫描controller,指定controller的包,但是view的包下面的注解类就扫描不到了,
    所以 com.test.springmvc.handlers 改成 com.test.springmvc就行啦,子目录都能扫描得到  -->
	<!--下面是 配置扫描  指定目录包 中的 java文件 -->
	<context:component-scan base-package="com.test.springmvc.handlers"></context:component-scan>

<!-- 配置视图解析器【多个函数可选择】: 此解析器默认生成InternalResourceView,如果项目添加了jstl.jar包的话就会自动改为生成JstlView; 即如何把 handler 方法返回值解析为实际的物理视图,handler的返回值也可以具体是 /WEB-INF/XXXX/XXX.jsp,这样下面的试图解析器就不需要添加前缀和后缀了 -->	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	


</beans>

4、编写控制器

在dispatcherServlet-servlet.xml中,因为标明了自定义扫描包路径,来寻找控制器。于是我们就用代码来实现控制器。在包名 com.test.springmvc.handlers 下,创建 HelloWorld.java

[java]
package com.test.springmvc.handlers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // spring里面 又名handler 处理器
public class HelloWorld {

/**
* 1、使用RequestMapping 注解来映射 请求的url
* 2、返回值会通过视图解析器解析为实际的物理视图,
* 对于org.springframework.web.servlet.view.InternalResourceViewResolver
* 会做如下解析,prefix + returnVal + suffix 得到实际的物理视图。然后做转发操作。
* 即 WEB-INF/views/success.jsp
*/
@RequestMapping("/helloworld")
public String hello(){
System.out.println("hello world");
return "success";
}

}

[/java]

上面的请求路径为:projectname/helloworld
也可以在类上面多加一个 RequestMapping 注解,比如 xxx
那么请求路径为 projectname/xxx/helloworld

五、配置 返回结果 页

/WEB-INF/views/success.jsp 编辑如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>



<h4> Success page </h4>



</body>
</html>

6、url请求测试结果

下面两个都能 正常访问
http://localhost:5858/springmvc-1/
http://localhost:5858/springmvc-1/index.jsp

WEB-INF只允许服务器访问,外部无法访问,所以 下面的 请求 无法访问。
http://localhost:5858/springmvc-1/WEB-INF/views/success.jsp

springmvc页面 能正常访问
http://localhost:5858/springmvc-1/helloworld

在index.jsp文件后面又新建了一个测试文件test1.jsp,发现能够正常访问。
http://localhost:5858/springmvc-1/test1.jsp

总结:在此web.xml配置的转发器的拦截域是:
经测试发现,先判断请求是不是 网站根目录/零层或多层路径/xxx.jsp,
如果是,查找是不是存在,存在的话就OK。不存在的就返回404
如果不是,那么再查看控制器,判断是否有匹配的映射名,如果仍没有就返回 404


补充:配置注解映射器和注解适配器

在配置转发器中,需要添加的注解映射器和注解适配器:
在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器。

在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。

在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter注解适配器。

在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器。

下面是在 配置转发器【即前面提到的dispatcherServlet-servlet.xml】 中,进行编辑的内容:

<!-- 注解映射器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <!-- 注解的适配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
    <!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置
        mvc:annotation-driven默认加载很多的参数绑定方法,
        比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
        实际开发时使用mvc:annotation-driven
     -->
    <!-- <mvc:annotation-driven></mvc:annotation-driven> -->

在实际使用时,不配置注解映射器和注解适配器,也能正常运行。因为MVC有默认配置在核心中的/org/springframework/web/servlet/DispatcherServlet.properties中,有兴趣的同学可以自行查看。


二、非注解情况配置

与注解情况的区别就是:1、修改handle     2、配置转发器文件

1、在springmvc.xml中的配置,如下:{也就是前面提到的dispatcherServlet-servlet.xml,因为配置文件可以随意取名}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	
     
<!-- 配置视图解析器【多个函数可选择】: 此解析器默认生成InternalResourceView,如果项目添加了jstl.jar包的话就会自动改为生成JstlView; 即如何把 handler 方法返回值解析为实际的物理视图,handler的返回值也可以具体是 /WEB-INF/XXXX/XXX.jsp,这样下面的试图解析器就不需要添加前缀和后缀了 -->	

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<!-- 配置jsp路径的前缀 -->
		<property name="prefix" value="/WEB-INF/views/"></property>
	    <!-- 配置jsp路径的后缀 -->
		<property name="suffix" value=".jsp"></property>
	</bean>
	
    <!-- 配置handler -->
     <bean id="helloController" name="/hello" class="com.test.springmvc.handlers.HelloController"></bean> 
    <!-- 处理器映射器  根据bean的name进行查找Handler 将controller 的url配置在bean的name中-->
     <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> 
    <!-- 处理器适配器  通过查看源码,可知此适配器能执行实现Controller接口的handler:-->
     <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> 

	
</beans>

所有的映射器都实现HandlerMapping接口,所有的适配器都实现HandlerAdapter接口,还有另外一个非注解的适配器:org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,另外一个映射器:org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,如果这些都不配置的话,仍然能够使用,因为MVC有默认配置在核心中的/org/springframework/web/servlet/DispatcherServlet.properties中,有兴趣的同学可以自行查看。

2、配置handler【又叫controller】

package com.test.springmvc.handlers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

//HelloController类必须实现Controller接口,查看映射文件:
//这样,springmvc 才能识别它

public class HelloController  implements Controller {

	@Override
	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
		
	//返回ModelAndView
        ModelAndView modelAndView =  new ModelAndView();
        //相当 于request的setAttribut,在jsp页面中通过itemsList取数据
        //在当前的实验中, 我没有在网页中进行获取信息测试,所以设置了空值
        modelAndView.addObject("itemsList", "");
        
        //指定视图 下面这句有错误 (正确做法是在 转发配置页中  删除视图解析器的前缀和后缀)
        modelAndView.setViewName("/WEB-INF/views/success.jsp");    
        //或者另一种正确的做法 上面这句返回"success";保留 转发配置页中的视图解析器的前缀和后缀

        //modelAndView.setViewName("success");
        
        System.out.println("hello world" + request.toString());
		request.toString();

        return modelAndView;
	}

}

springmvc基础-配置开发环境

一、从官网下载  spring  jar包

http://repo.spring.io/release/org/springframework/

楼主选的是spring 4 版本,开发要用到的 基本jar包如下:

-javadoc 其实里面就是 html,用于介绍 api 的说明文档
-sources 里面是源代码,用于查看 框架的 设计和实现

二、 eclipse 新建 dynamic web 项目

记得勾选自动生成 web.xml文件啊。。。

三、编辑web.xml文件,创建springmvc文件

先了解一下web.xml文件

web.xml文件进行配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
	
	<!-- 配置 DispatcherServlet -->
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
		<!-- 
			实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
			默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
		-->
		<!--  
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
		-->
		<load-on-startup>1</load-on-startup>
        <!-- load on starup 大于、等于0代表随系统启动后加载 ,数字越小优先级越高 ; 数值为负数时,启动时不加载,调用时再加载 -->
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
     <!-- 
              第一种:*.action 或者 *.do,访问以.action或*.do结尾 由DispatcherServlet进行解析
              第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析
              使用此种方式可以实现 RESTful风格的url
              第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
              仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错。
       -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>

<!-- 下面这个没有写不要紧,因为tomcat服务器默认 index为首页 --> 
<welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>

</web-app>

可以参考一下:
j2ee标准-web.xml中load-on-startup源码解析

四、编辑 spring mvc 配置文件

根据 web.xml 中 已经配置好的 org.springframework.web.servlet.DispatcherServlet 的类,加载这个类时需要 转发配置文件。

转发配置文件–>【前面web.xml配置的是在src下, 取名springmvc.xml文件。如果是默认配置应该在WEB-INF下即和web.xml同目录,取名dispatcherServlet-servlet.xml文件】,其中 配置自定扫描的包 ,是为了扫描指定包文件中,有@Controller ,@Component等注解的类文件。

转发配置文件 的基本配置 如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

    <!-- 对于注解的Handler可以单个配置 实际开发中建议使用组件扫描 -->
    <!-- <bean class="com.test.springmvc.handlers.HelloWorld" />  -->
    <!-- 可以扫描controller、service、...这里让扫描controller,指定controller的包,但是view的包下面的注解类就扫描不到了,
    所以 com.test.springmvc.handlers 改成 com.test.springmvc就行啦,子目录都能扫描得到  -->
	<!--下面是 配置扫描  指定目录包 中的 java文件 -->
	<context:component-scan base-package="com.test.springmvc"></context:component-scan>
    
	<!-- 配置视图解析器【多个函数可选择】: 此解析器默认生成InternalResourceView,如果项目添加了jstl.jar包的话就会自动改为生成JstlView; 即如何把 handler 方法返回值解析为实际的物理视图,handler的返回值也可以具体是 /WEB-INF/XXXX/XXX.jsp,这样下面的试图解析器就不需要添加前缀和后缀了 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
</beans>

1、自定义扫描目录范围表示 扫描指定文件,查看是否 存在 注解。
2、springmvc 配置文件的 视图解析器,告诉了 控制器 返回结果时,应该将页面跳转到 哪里。比如 控制器【第五节介绍】 返回结果 success ,那么跳转页面 应该到 /WEB-INF/views/success.jsp 文件中

四、配置 控制器

在包名 com.test.springmvc.handlers 下
创建 HelloWorld.java

[java]
package com.test.springmvc.handlers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // spring里面 又名handler 处理器
public class HelloWorld {

/**
* 1、使用RequestMapping 注解来映射 请求的url
* 2、返回值会通过视图解析器解析为实际的物理视图,
* 对于org.springframework.web.servlet.view.InternalResourceViewResolver
* 会做如下解析,prefix + returnVal + suffix 得到实际的物理视图。然后做转发操作。
* 即 WEB-INF/views/success.jsp
*/
@RequestMapping("/helloworld")
public String hello(){
System.out.println("hello world");
return "success";
}

}

[/java]

上面的请求路径为:projectname/helloworld
也可以在类上面多加一个 @RequestMapping 注解,比如 xxx
那么请求路径为 projectname/xxx/helloworld

五、配置 返回结果 页

/WEB-INF/views/success.jsp 编辑如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>


<h4> Success page </h4>


</body>
</html>
六、启动服务器,输入地址

在配置控制器时,已经 在类HelloWorld 使用了@RequestMapping(“/helloworld”)注解
所以 浏览器 直接输入 ” http://localhost:端口号/工程名/helloworld” 就能看到结果啦

七、补充:配置注解映射器和注解适配器

在配置转发器中,需要添加的注解映射器和注解适配器:
在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping注解映射器。

在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping注解映射器。

在spring3.1之前使用org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter注解适配器。

在spring3.1之后使用org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter注解适配器。

下面是在 配置转发器【即前面提到的dispatcherServlet-servlet.xml】 中,进行编辑的内容:

<!-- 注解映射器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <!-- 注解的适配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
    <!-- 使用 mvc:annotation-driven代替上边注解映射器和注解适配器配置
        mvc:annotation-driven默认加载很多的参数绑定方法,
        比如json转换解析器就默认加载了,如果使用mvc:annotation-driven不用配置上边的RequestMappingHandlerMapping和RequestMappingHandlerAdapter
        实际开发时使用mvc:annotation-driven
     -->
    <!-- <mvc:annotation-driven></mvc:annotation-driven> -->

在实际使用时,不配置注解映射器和注解适配器,也能正常运行。因为MVC有默认配置在核心中的/org/springframework/web/servlet/DispatcherServlet.properties中,有兴趣的同学可以自行查看。

八、非注解方式配置方法

参考springmvc基础-注解方式编程和非注解方式编程

九、国际化配置方法

1、新建i18n文件,存放于src文件夹下面:

i18n.username=Username
i18n.password=Password
i18n.username=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
i18n.username=Username
i18n.password=Password

2、在转发配置文件中添加:

<!-- 配置国际化资源文件 -->
<bean id="messageSource"
	class="org.springframework.context.support.ResourceBundleMessageSource">
	<property name="basename" value="i18n"></property>	
</bean>

3、在请求页面jsp中:添加

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<body>
	<fmt:message key="i18n.username" ></fmt:message>
	<fmt:message key="i18n.password" ></fmt:message>
</body>

国际化问题特别注意点:<fmt:message>标签只能出现在转发页面中,不能出现在请求页面中【请求页面我测试了一下,出现乱码】原因是在转发配置页中,指定了国际化配置文件啊,必须在转发过程中才会生效啊 !!!

十、配置无控制器直接转发

一般情况下,请求页需要由控制器处理后,才能转发到目标页面。但是也可以编辑转发配置文件,实现不经过控制器而直接转发到目标页面。【本质上就是在这里配置了一个默认控制器,path代表控制器的@RequestMaping  ,view-name代表 控制器的返回值】

<!-- 配置直接转发的页面 -->
	<!-- 可以直接相应转发的页面, 而无需再经过 Handler 的方法.  -->
	<mvc:view-controller path="/success" view-name="success"/>

但是上面的配置会造成,那些所有需要经过控制器转发的url请求,全部失效无法访问。为了解决这个问题,需要在转发配置页  继续配置如下:

<mvc:annotation-driven></mvc:annotation-driven>

十一、请求静态资源问题

因为前面测试过了,如果请求路径是xxx.jsp是可以直接显示的。如果是XXX,就会交由handler处理。

为了能够在网页中,加载js和css,请求静态资源,所以需要  编辑   转发配置器。

 <!--  
		default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
		它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 
		Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理

		一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
		若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
		
	-->
	<mvc:default-servlet-handler/>
	<!-- 前面的 mvc:default-servlet-handler 可以解决静态资源了,但是让其他经过handler的请求,又失效了,所以配置了下面的 mvc:annotation-driven 来解决问题 -->
	<mvc:annotation-driven></mvc:annotation-driven>	
	

出现过的问题小结:之前在项目中添加了entity文件,里面有@NotEmpty注解等。这些注解用到了校验jar包,比如 hibernate-validator-5.0.0.CR2.jar

在没有添加这句话  <mvc:annotation-driven></mvc:annotation-driven> 前,一直没有报错,后来加了这句话后报错了。

Caused by: java.lang.NoClassDefFoundError: org/jboss/logging/BasicLogger
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2541)
	at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:858)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1301)
	at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1166)
	at org.hibernate.validator.internal.util.logging.LoggerFactory.make(LoggerFactory.java:29)
	at org.hibernate.validator.internal.util.Version.<clinit>(Version.java:27)
	at org.hibernate.validator.internal.engine.ConfigurationImpl.<clinit>(ConfigurationImpl.java:65)
	at org.hibernate.validator.HibernateValidator.createGenericConfiguration(HibernateValidator.java:41)
	at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:276)
	at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:250)
	at org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean.afterPropertiesSet(OptionalValidatorFactoryBean.java:40)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
	... 28 more

发现是缺少了  jboss-logging的jar包,添加了就行。


springmvc概述-知识大纲整理

Spring基础-springmvc知识大纲归纳整理

一、原理

1.用户发出HTTP请求

2-3.DispatchServlet通过请求URL到HandlerMapping中去寻找相对应的Handler并返回给DispatchServlet。

4.找到Handler之后,通过Handler获得Controller类对象并调用。

5.Controller 去调用业务处理的xxxService并返回ModelAndView。

6.DispatchServlet把获得的ModelAndView交给ViewResovler进行处理,然后将处理的结果封装成View返回给DispatchServlet。

7.将View视图解析

8.发送给浏览器进行显示。


二、结构

spring-webmvc-版本号.jar 是springmvc的 核心包,springmvc框架中 有jstl的jar包,因为SpringMVC默认使用的标签就是jstl标签。

 

spring特点-DI依赖注入实现原理

以前一直有个疑惑,为什么我创建的controller中注入的service类有时候是代理类,有时候是普通javabean,当时能力不够,现在已经有了点经验就大胆跟了跟源码,看看到底咋回事。

首先看看问题现象:

a1:service是代理类,并且是CGLIB类型代理  

 

a2:service是代理类,并且是jdk 动态代理

b:serivce不是代理类,而是普通类

问题分析

我对service类进行了以下的测试:(前提开启事务注解<tx:annotation-driven/>)

1)service方法添加@Transactional注解或者加入其它的aop拦截配置,没有实现任何接口。   对应问题现状 a1

2)service方法添加@Transactional注解或者加入其它的aop拦截配置,实现了接口。              对应问题现状a2

3)serice方法没有添加@Transactional注解或者其它的aop拦截配置。                                      对应问题现状b

看来出现这种问题的原因就是spring的问题,因为这个类是它创建的,这就需要我们来看下spring创建bean的代码,由于spring太庞大了

我们只看最关键的部分,在创建bean是都会调用getBean()方法,

@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
       @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    return createBean(beanName, mbd, args);
}

经过不断的流转会进入AbstractAutowireCapableBeanFactory的createBean方法

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
     try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
               logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
      }
      catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
          // A previously detected exception with proper bean creation context already,
          // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
          throw ex;
      }

 }

然后调用doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
if (instanceWrapper == null) {
       
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }return exposedObject;
    }

然后进入核心的createBeanInstance方法,省去了不相关方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
}

然后调用instantiateBean进行bea的实例化

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

实例化时会调用SimpleInstantiationStrategy的instantiate方法

@Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

通过上面的代码分析,如果Bean定义中没有方法覆盖,就使用JDK的反射机制进行实例化,否则,使用CGLIB进行实例化。

1、通过CGLIB的方式instantiateWithMethodInjection(bd, beanName, owner)
2、或者java的反射方式BeanUtils.instantiateClass(constructorToUse)
实例化一个bean

这是时候都是一个纯洁无瑕的javabean,那每个bean的额外加工,例如为某个bean添加事务支持,添加aop配置,还有就是将springmvc的controller进行url和handler的映射,等等这些都是在spring的扩展点完成的,回到
上面的doCreateBean方法,执行完实例化bean后执行
1、populateBean(beanName, mbd, instanceWrapper);
其中的populateBean是为了给生成的bean装配属性,
2、initializeBean(beanName, exposedObject, mbd);
这不是我们这次讨论的重点,关键是initializebean方法 ,为了配置bean的拓展方法 aop 事务等

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

这个方法就是对生成的bean进行一些扩展处理,主要就是这个方法会调用我们自定义的扩展点
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到这里是获取所有的beanProcessor,调用postProcessAfterInitialization方法,我们要关注是的一个叫InfrastructureAdvisorAutoProxyCreator的扩展类。

/**
 * Auto-proxy creator that considers infrastructure Advisor beans only,
 * ignoring any application-defined Advisors.
 *
 * @author Juergen Hoeller
 * @since 2.0.7
 */
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {}

看下这个类的注释可以发现这个类是为配置了aop配置(包括注解和xml配置两种方式)的类,生成代理类。
核心方法是下面这个方法wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

下面解析下这个函数

首先看下getAdvicesAndAdvisorsForBean这个方法:名字很明显用来获取当前bean的advisor和adices的,这些都是生成代理类时需要的信息。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

然后调用findEligibleAdvisors,获取配置的advisor信息

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

来看下findCandidateAdvisors方法,最终调用BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<>();
        }

        List<Advisor> advisors = new LinkedList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                      throw ex;
                    }
                }
            }
        }
        return advisors;
    }

1)首先获取spring管理的Advisor类型的类名称。

2)通过beanFactory获取该bean对应的实体类,并装入advisors。

生成的这个advisor可是相当复杂,这里我们以事务advisor为例说明

可以看到这个advisor包含了advice(aop中的通知),pointcut(aop中的切入点),


advice是TransactionInterceptor,这个通知是用来管理spring的事务的可以看到包含事务的管理器等管理事务的属性,具体的方法见TransactionAspectSupport.invokeWithinTransaction
pointcut是TransactionAttributeSourcePointcut,

public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

这个是pointcut的核心方法,用来匹配某个类是否符合事务管理的aop拦截要求。
ok,回到之前的wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

我们之前分析道getAdvicesAndAdvisorsForBean方法,可以看到如果得到的结果是DO_NOT_PROXY,就会将这个bean直接返回,

如果不是DO_NOT_PROXY,(其实DO_NOT_PROXY就是null,但是使用DO_NOT_PROXY会使得代码逻辑更加清晰),就会执行

createProxy方法,创建一个代理类,然后返回一个代理类,ok,现在我们就清楚了问题分析中的 第3)和第 1) 2) 区别,那就是

service类是否配置了相关的aop拦截配置,无论是注解还是xml形式,目前我们还不清楚第1)和 第2)的区别,就是为什么有时候

生成jdk代理,有时候生成cglib代理,这就需要继续向下看creatProxy方法了,最终会进入一个DefaultAopProxyFactory的createAopProxy

方法:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

如果目标类是接口就一定会使用jdk代理,如果目标类没有可以代理的接口就一定会使用Cglib代理。

问题总结

这个问题我们现在知道了,那他有什么意义呢,换句话说,我们为什么要知道这个,即使不知道原理,我们也可以去搜搜去解决,在我看来
把他弄明白的过程学会了很多知识,而且我们如果在工作过程中遇到了需要扩展的地方,我们可以很容易的去解决。

最后欢迎大家在评论区留言,有什么想法说出来,共同进步。

来自:https://www.cnblogs.com/zcmzex/p/8822509.html

 

 

参考另一篇IOC源码理解分析:

总结:

//AbstractAutowireCapableBeanFactory创建Bean实例对象:
//AbstractAutowireCapableBeanFactory类实现了ObejctFactory接口,创建容器指定的Bean实例对象,同时还对创建的Bean实例对象进行初始化处理。
//  创建Bean实例对象  方法中 调用了 核心 三句话  
   protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)  
  
//调用一 :createBeanInstance:生成Bean所包含的java对象实例。
          instanceWrapper = createBeanInstance(beanName, mbd, args);  
//调用二:populateBean :对Bean属性的依赖注入进行处理(解析bean的属性,并对bean的属性赋值[值注入])
          populateBean(beanName, mbd, instanceWrapper);  
//调用三:对 bean 的 扩展功能 进行配置 比如 aop ,事务。(可以 插入到任何操作 后)    
// BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件  
        exposedObject = initializeBean(beanName, exposedObject, mbd);  

// 创建bean的 过程 都是 普通的 实例bean ,如果 是接口 就执行 不创建。
// 然后  bean 赋值 过程 没有变化,
// 到了 bean 拓展功能阶段,就看 有没有 代理 或者 接口问题了。比如 上面的 举例  明天写后续

spring特点-DI依赖注入三种方式

一、Spring 容器 自动创建的Bean的方法有两种:

1、在xml文件配置 (即spring 配置文件)
在xml文件中 配置bean, 标注 各个bean之间的依赖关系。
2、(1)在代码中用注解:
在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。

constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。

byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

byType:查找所有的set方法,将符合符合参数类型的bean注入。

下面进入正题:注解方式注册bean,注入依赖

主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
@Component:可以用于注册所有bean
@Repository:主要用于注册dao层的bean
@Controller:主要用于注册控制层的bean
@Service:主要用于注册服务层的bean

描述依赖关系主要有两种:
@Resource:java的注解,默认以byName的方式去匹配与属性名相同的bean的id,如果没有找到就会以byType的方式查找,如果byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。

@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;

public UserService(){

}

@Autowired:spring注解,默认也是以byName的方式去匹配与属性名相同的bean的id,如果没有找到,就通过byType的方式去查找,如果查找到多个,用@Qualifier注解限定具体使用哪个。

@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;<br>

(2)同时在spring 配置文件中 配置自动扫描的包:

 <!-- 配置自动扫描的包 -->
 <context:component-scan base-package="com.webproject.srcdir">             
 </context:component-scan>   <br>
在xml配置了这个标签后,spring可以自动去扫描base-pack下面或者子包下面的java文件,如果扫描到有@Component @Controller@Service等这些注解的类,则把这些类注册为bean
注意:如果配置了<context:component-scan>那么<context:annotation-config/>标签就可以不用再xml中配置了,因为前者包含了后者。另外<context:component-scan>还提供了两个子标签

1.     <context:include-filter>
2.     <context:exclude-filter>

在说明这两个子标签前,先说一下<context:component-scan>有一个use-default-filters属性,改属性默认为true,这就意味着会扫描指定包下的全部的标有@Component的类,并注册成bean.也就是@Component的子注解@Service,@Reposity等。所以如果仅仅是在配置文件中这么写
<context:component-scan base-package="com.webproject.srcdir"/>

 Use-default-filter此时为true那么会对base-package包或者子包下的所有的进行java类进行扫描,并把匹配的java类注册成bean。


 可以发现这种扫描的粒度有点太大,如果你只想扫描指定包下面的Controller,该怎么办?此时子标签<context:incluce-filter>就起到了勇武之地。如下所示

<context:component-scan base-package="com.webproject.srcdir.controller">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

这样就会只扫描base-package指定下的有@Controller下的java类,并注册成bean

但是因为use-dafault-filter在上面并没有指定,默认就为true,所以当把上面的配置改成如下所示的时候,就会产生与你期望相悖的结果(注意base-package包值得变化)

<context:component-scan base-package="com.webproject.srcdir">  

<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>   

</context:component-scan>  

此时,spring不仅扫描了@Controller,还扫描了指定包所在的子包service包下注解@Service的java类

此时指定的include-filter没有起到作用,只要把use-default-filter设置成false就可以了。这样就可以避免在base-packeage配置多个包名这种不是很优雅的方法来解决这个问题了。

另外在我参与的项目中可以发现在base-package指定的包中有的子包是不含有注解了,所以不用扫描,此时可以指定<context:exclude-filter>来进行过滤,说明此包不需要被扫描。综合以上说明

Use-dafault-filters=”false”的情况下:<context:exclude-filter>指定的不扫描,<context:include-filter>指定的扫描

写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码嘛。

二、Spring 依赖注入 三种方式:

(Dependency Injection) 依赖注入:两个对象之间的依赖关系在程序运行时由外部容器动态的注入依赖行为方式称为依赖注入(DI) 。 DI 是 IOC 的一种形式。

IoC 在应用开发中是一个非常有力的概念。如 Martin Flower 所述, IoC的一种表现形式就是依赖性注射。依赖性注射用的是好莱坞原则, ” 不要找我,我会找你的。 “。换句来说,你的类不会去查找或是实例化它们所依赖的类。控制恰好是反过来的,某种容器会设置这种依赖关系。使用 IoC常常使代码更加简洁,并且为相互依赖的类提供一种很好的方法。

依赖注入的三种实现类型:接口注入、 Setter 注入和构造器注入。
1、接口注入:

public class ClassA {
private InterfaceB clzB;
public void doSomething() {
Ojbect obj=Class.forName(Config.BImplementation).newInstance();
clzB = (InterfaceB)obj;
clzB.doIt()
}
……
}

上面的代码中, ClassA 依赖于 InterfaceB 的实现,如何获得 InterfaceB实现类的实例?传统的方法是在代码中创建 InterfaceB 实现类的实例,并将起赋予 clzB 。
而这样一来, ClassA 在编译期即依赖于 InterfaceB的实现。为了将调用者与实现者在编译期分离,于是有了上面的代码.
我们根据预先在配置文件中设定的实现类的类名 (Config.BImplementation) ,动态加载实现类,并通过InterfaceB 强制转型后为 ClassA 所用。这就是接口注入的一个最原始的雏形。
而对于一个 Type1 型 IOC 容器而言,加载接口实现并创建其实例的工作由容器完成,见如下代码。

public class ClassA {
private InterfaceB clzB;
public Object doSomething(InterfaceB b) {
clzB = b;
return clzB.doIt();
}
……
}

在运行期, InterfaceB 实例将由容器提供。
Type1 型 IOC 发展较早(有意或无意),在实际中得到了普遍应用,即使在 IOC的概念尚未确立时,这样的方法也已经频繁出现在我们的代码中。
下面的代码大家应该非常熟悉:

public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request,HttpServletResponseresponse)throws ServletException, IOException { …… } }

这也是一个 接口注入, HttpServletRequest 和 HttpServletResponse 实例由Servlet Container 在运行期动态注入。
参考 J2EE API文档: https://docs.oracle.com/javaee/6/api/
HttpServletRequest HttpServletRequest 都是个接口。
这两个接口是由各个 j2ee厂商提供的实现的。比如tomcat ,weblogic都有自己的j2ee实现。
可以说j2ee只是一堆规范和定义好的接口。实现是由J2EE厂商实现,实例化是由web容器实例化

java通过 动态代理 反射实列化接口,以此实现 接口注入。

2、set注入:
采用属性的set方法进行初始化,就成为set注入。
1)给普通字符类型赋值。

public class User{
   privateString username;
 
   publicString getUsername() {
       returnusername;
   }
   publicvoid setUsername(String username) {
      this.username= username;
   }
}

我们只需要提供属性的set方法,然后去属性文件中去配置好让框架能够找到applicationContext.xml文件的beans标签。标签beans中添加bean标签, 指定id,class值,id值不做要求,class值为对象所在的完整路径。bean标签再添加property 标签,要求,name值与User类中对应的属性名称一致。value值就是我们要给User类中的username属性赋的值。

<bean id="userAction"class="com.test.spring.action.User" >
<property name="username" value="admin"></property>
</bean>

2)给对象赋值

同样提供对象的set方法

public class User{
     private UserService userservice;
     public UserServicegetUserservice() {
          returnuser;
     }
     public void setUserservice(UserService userservice){
         this.userservice= userservice;
     }
}

配置文件中要增加UserService的bean标签声明及User对象对UserService引用。

<!--对象的声明-->
<bean id="userService" class="com.test.spring.service.UserService"></bean>
 
<bean id="userAction"class="com.test.spring.action.User" >
   <property name="userservice" ref="userService"></property>
</bean>

这样配置,框架就会将UserService对象注入到User类中。

3)给list集合赋值

同样提供set方法

public class User{
    privateList<String> username;
    publicList<String> getUsername() {
        returnusername;
    }
    publicvoid setUsername(List<String> username) {
        this.username= username;
    }
}
<bean id="userAction"class="com.test.spring.action.User" >
     <propertyname="username">
           <list>
               <value>zhang,san</value>
               <value>lisi</value>
               <value>wangwu</value>                                
               </list>
    </property>
</bean>

4)给属性文件中的字段赋值

public class User{
    privateProperties props ;
    publicProperties getProps() {
        returnprops;
    }
    publicvoid setProps(Properties props) {
        this.props= props;
    }
}
<bean>
    <propertyname="props">
        <props>
           <propkey="url">jdbc:oracle:thin:@localhost:orl</prop>
           <propkey="driverName">oracle.jdbc.driver.OracleDriver</prop>
           <propkey="username">scott</prop>
           <propkey="password">tiger</prop>
        </props>
    </property>
</bean>

标签中的key值是.properties属性文件中的名称

注意:

无论给什么赋值,配置文件中标签的name属性值一定是和对象中名称一致。

3、构造方法注入

1)构造方法一个参数

public class User{
    privateString usercode;
    publicUser(String usercode) {
        this.usercode=usercode;
    }
}
<bean id="userAction"class="com.test.spring.action.User">              
    <constructor-argvalue="admin"></constructor-arg>                    
</bean>

2)构造函数有两个参数时

当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。
当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置

<constructor-argvalue="admin"index="0"></constructor-arg>                
<constructor-argvalue="23" type="int"index="1"></constructor-arg><br>

这样制定,就是构造函数中,第一个参数为string类型,第二个参数为int类型。

参考自:https://blog.csdn.net/sinat_34093604/article/details/52485270
https://blog.csdn.net/lishuangzhe7047/article/details/20740835#
https://blog.csdn.net/a909301740/article/details/78379720

spring整合基础-spring和springMVC父子容器的原理

要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的。spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

说完了spring上下文的初始化过程,这三个上下文的关系应该就了解了。如还是不太清楚,我就爱莫能助了,只能自行看代码去了。

===============================================================================================================

最近在做项目时牵扯到有关父子上下文的概念。

何为父子上下文呢?

父上下文:

使用listener监听器来加载配置文件,如下:

<listener>   

  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   

</listener>

Spring 会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

子上下文:

使用Spring MVC 来处理拦截相关的请求时,会配置DispatchServlet:

<servlet>

    <servlet-name>dispatcherServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet

    </servlet-class>

    <init-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>

 
<servlet-mapping>

    <servlet-name>dispatcherServlet</servlet-name>

    <url-pattern>/</url-pattern>

</servlet-mapping>

每个DispatchServlet会有一个自己的上下文,称为子上下文,它也保存在 ServletContext中,key 是”org.springframework.web.servlet.FrameworkServlet.CONTEXT”+Servlet名称。当一 个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + “.CONTEXT”。

可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);

父上下文(父容器)和子上下文(子容器)的访问权限:

子上下文可以访问父上下文中的bean,但是父上下文不可以访问子上下文中的bean。

父上下文使用与否

方案一,传统型:

父上下文容器中保存数据源、服务层、DAO层、事务的Bean。

子上下文容器中保存Mvc相关的Action的Bean.

事务控制在服务层。

由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。

当然,做为“传统型”方案,也没有必要这要做。

 

方案二,激进型:

Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了—–没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。

由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。

 

 

 

Java–大项目能做好–按传统方式做,规规矩矩的做,好扩展,好维护。

Java–小项目能做快–按激进方式做,一周时间就可以出一个版本,先上线接受市场(用户)的反馈,再改进,再反馈,时间就是生命(成本)。

 

来自:https://blog.csdn.net/caomiao2006/article/details/51290494

spring特点-IOC容器启动的三种方式

前期准备工作

使用IDE:IDEA

创建项目时需要用到的框架有Java和Web

就像下图的进行勾选。

创建项目完成之后的目录

如下图所示。

创建项目时,gradle中用到的依赖:

// Java EE 通过本地Jar包导入
compile fileTree(dir:'lib',include:['*.jar'])
// Spring 框架
def springVersion = '4.3.10.RELEASE'
compile("org.springframework:spring-aop:$springVersion",
            "org.springframework:spring-orm:$springVersion",
            "org.springframework:spring-jdbc:$springVersion",
            "org.springframework:spring-instrument-tomcat:$springVersion",
            "org.springframework:spring-instrument:$springVersion",
            "org.springframework:spring-framework-bom:$springVersion",
            "org.springframework:spring-expression:$springVersion",
            "org.springframework:spring-core:$springVersion",
            "org.springframework:spring-context-support:$springVersion",
            "org.springframework:spring-context:$springVersion",
            "org.springframework:spring-beans:$springVersion",
            "org.springframework:spring-aspects:$springVersion",
            "org.springframework:spring-test:$springVersion",
            "org.springframework:spring-tx:$springVersion",
            "org.springframework:spring-web:$springVersion",
            "org.springframework:spring-webmvc:$springVersion",
            "org.springframework:spring-webmvc-portlet:$springVersion")
// 模拟测试框架
testCompile "org.mockito:mockito-core:2.+"
// 集成测试框架
testCompile 'org.testng:testng:6.8.17'
// 单元测试框架
testCompile group: 'junit', name: 'junit', version: '4.12'

还需要的框架有:Java EE

通过BeanFactory启动IOC容器

BeanFactory类是Spring框架下最核心的接口,它提供了高级的IOC配置机制。我们可以使用下面的XmlBeanDefinitionReader和DefaultListableBeanFactory类启动IOC容器。

在com.czuaphe.test包下,创建Car类

bean文件入在类路径(classpath)下

在Resource目录编写beans.xml文件生成实例Bean

然后,在test/java目录下,使用TestNG框架测试一个BeanFactoryTest测试类
通过加载Resource下的beans.xml文件启动IOC容器,得到实例Bean。

上图我们是通过类路径得到XML文件的,在gradle下,类路径就是resources目录下

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 通过类路径得到资源,以classpath:开头
Resource resource = resolver.getResource("classpath:beans.xml");

运行结果如下图所示:

bean文件放在文件系统(FileSystem)目录下

如果我们将beans.xml文件放在与Car类相同的路径下:

在test/java目录下,BeanFactoryTest类中的resouce变量只需要改变一下,就可以达到和上面一样的结果。

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource resource = resolver.getResource("file:src/main/java/com/czuaphe/test/beans.xml");

通过ApplicationContext类启动IOC容器

ApplicationContext类继承了BeanFactory类,实现了更多面向应用的功能。

我们就在上例的基础上,在test/java目录下,使用TestNG框架再编写一个ApplicationContextTest类。同样可以实现像上面两种加载XML文件启动IOC容器的方法,此外,ApplicatioinContext类还可以通过类注解的方式加载Bean启动IOC容器。

我们实现通过类注解的方式,实现启动IOC容器。首先,在com.czuaphe.test包下写Bean.java文件,编写实例化Bean的配置。

在ApplicationContextTest类下,实现通过加载类注解配置文件启动IOC容器。

另外两种通过ApplicationContext类启动IOC容器,也在注释中写出了。

通过类注解启动IOC容器,运行测试的结果如下图所示:

通过WebApplicationContext类启动IOC容器

WebApplicationContext类继承了ApplicationContext类,专门用于Web应用。启动方式也是通过Web容器启动,实现IOC容器的启动。不能简单通过Java代码实现启动。

在webapp/WEB-INF下的web.xml中定义ContextLoaderListener监听Web容器的启动,然后定义contextConfigLocation获得Spring框架的Bean配置文件。

然后在web.xml文件中,再定义一个Servlet,定义它的属性load-on-startup为1,表示在Web容器和Spring框架的IOC容器启动之后自启动,得到WebApplicationContext类的实例,就可以得到Bean实例了。

web.xml文件如下图所示

在com.czuaphe.webtest包下,编写SpringBeanServlet类,重写Servlet的init()方法,得到WebApplicationContext类的实例。

最后,对此项目配置Tomcat服务器,在启动Web容器时,就会启动Spring的IOC容器,而且会运行自定义的SpringBeanServlet,得到Bean实例。运行结果如下图所示:

总结

编写了三种IOC容器的启动方式,在非Web应用下,使用ApplicationContext启动IOC容器就已经足够使用,在Web应用下,就必须在web.xml文件中配置WebApplicationContext的启动。让Web容器启动时IOC容器也启动。BeanFactory类启动IOC容器一般不常用。

来自:https://blog.csdn.net/cuipp0509/article/details/78544497#%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E6%97%B6%E9%9C%80%E8%A6%81%E7%94%A8%E5%88%B0%E7%9A%84%E6%A1%86%E6%9E%B6%E6%9C%89java%E5%92%8Cweb

spring特点-AOP(面向切面示例)

什么是AOP?
基本概念
切面(aspect):横切关注点被模块化的特殊对象。
通知(advice):切面必须要完成的工作。切面中的每个方向称之为通知。通知是在切面对象中的。
目标(target):被通知的对象。
代理(proxy):向目标对象应用通知后创建的对象。

连接点(joinpoint):目标对象的程序执行的某个特定位置。如某个方法调用前,调用后的位置。包括两个信息:1.目标程序的哪个方法?2.方法执行
前还是执行后?
切点(pointcut):每个类会有多个连接点,AOP通过切点定位到特定的边接点。
类比,连接点相当于数所成方圆 中的记录,而切点相当于查询条件。一个切点匹配多个连接点。

 

使用XML配置的方法:

1.导入 spring包和aspectj的两个jar包

2.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test;

public interface IHuman {
    public void chifan();
    public void shuijiao();

}

实现接口的两个类:

package com.itnba.test;

import org.springframework.stereotype.Component;

public class Chinese implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("中国人吃饭");

    }
    
    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("中国人睡觉");
    }

}
package com.itnba.test;

import org.springframework.stereotype.Component;


public class American implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("美国人吃饭");

    }

    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("美国人睡觉");
    }


}

3.定义一个切面类

package com.itnba.test;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
public class Qiemian {
    
    public void chifanqian(){
        System.out.println("洗手");
    }
    public void chifanhou(){
        System.out.println("漱口");
    }
    public void shuijiaoqian(){
        System.out.println("洗澡");
        
    }

}

4.XML文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"
        default-autowire="byName"
        >
         <!-- 首相要实例化目标对象类和切面类 -->
        <bean id="chinese" class="com.itnba.test.Chinese"></bean>
        <bean id="american" class="com.itnba.test.American"></bean>
        <bean id="qiemian" class="com.itnba.test.Qiemian"></bean>
        
        <aop:config>
        <!-- 要切入的对象 -->                 
        <aop:pointcut expression="execution(* com.itnba.test.*.chifan(..))" id="chifan"/>
        <aop:pointcut expression="execution(* com.itnba.test.*.shijiao(..))" id="shijiao"/>
        <!-- 切入点 -->
        <aop:aspect id="ha" ref="qiemian"><!-- 切面类  -->
            <!--  <aop:之前before、之后after... method="切面类中的方法" pointcut-ref="上面的切入的对象"/>  -->
            <aop:before method="chifanqian()" pointcut-ref="chifan"/><!-- 之前通知 -->
            <aop:after method="chifanhou()()" pointcut-ref="chifan"/><!-- 之后通知 -->
            <aop:before method="shuijiaoqian()" pointcut-ref="shijiao"/>
        </aop:aspect>
    </aop:config>
</beans>

5.运行

package com.itnba.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //要用接口接收
        IHuman c= (IHuman)context.getBean("chinese");
        c.chifan();
        c.shuijiao();
        System.out.println("*********************************");
        //要用接口接收
        IHuman a= (IHuman) context.getBean("american");
        a.chifan();
        a.shuijiao();

    }

}

结果:(主要是第4步的xml配置中 ,引用 shuijiao  错打成了  shijiao)

使用注解的方法:

1.导入 spring包和aspectj的两个jar包

2.配置文件中加入aop命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" >

        <!-- 自动扫描包下的类,并将其实例化。多个包之间用,隔开 -->
        <context:component-scan base-package="com.itnba.test"></context:component-scan>
        <!-- 配置文件中启动AspectJ的注解功能 ,默认是false,要将其改为true -->
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> 
</beans>

3.目标对象(要切入的对象)

首先为了不违反开闭原则和更好的可扩展性,目标对象实际上是实现了已定义好的某个接口

接口:

package com.itnba.test;

public interface IHuman {
    public void chifan();
    public void shuijiao();

}

实现接口的两个类:

package com.itnba.test;

import org.springframework.stereotype.Component;

@Component
public class Chinese implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("中国人吃饭");

    }
    
    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("中国人睡觉");
    }

}
package com.itnba.test;

import org.springframework.stereotype.Component;

@Component
public class American implements IHuman {

    @Override
    public void chifan() {
        // TODO 自动生成的方法存根
        System.out.println("美国人吃饭");

    }

    @Override
    public void shuijiao() {
        // TODO 自动生成的方法存根
        System.out.println("美国人睡觉");
    }


}

4.定义一个切面类,直接在里面注解

package com.itnba.test;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect //声明该类是切面类
@Component//配置文件中启动自动扫描功能
public class Qiemian {
    
    @Before("execution(* com.itnba.test.*.chifan(..))")//在那个方法的之前通知    *代表全部..代表所有形参,不管有多少
    public void chifanqian(){
        System.out.println("洗手");
    }
    @After("execution(* com.itnba.test.*.chifan(..))")//在那个方法之后通知
    public void chifanhou(){
        System.out.println("漱口");
    }
    @Before("execution(* com.itnba.test.*.shuijiao(..))")
    public void shuijiaoqian(){
        System.out.println("洗澡");
        
    }

}

5.写好后用main函数来运行一下

package com.itnba.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Chinese c= context.getBean(Chinese.class);
        c.chifan();
        c.shuijiao();
        System.out.println("*********************************");
        American a= (American) context.getBean("american");
        a.chifan();
        a.shuijiao();

    }

}

结果:

这样,Spring的面向切面编程就简单介绍完了。

通知分类:
前置通知(@Before) — 在目标方法执行前执行的通知。
后置通知(@After) — 在目标方法执行后执行的通知。无论是否发生异常。后置通知中,无法读取目标方法返回的结果。
返回通知(@AfterReturnning) –在目标方法执行成功后执行的通知。在返回通知中可以访问目标返回结果的。
@AfterReturnning(value=”execution(* com.itnba..*(..))”,returning=”result”)
public void afterRun(Object result){
System.out.println(result);
}

异常通知(@AfterThrowing) — 在目标方法执行出错后执行的通知。
@AfterThrowing(value=”execution(* com.itnba..*(..))”,throwing=”ex”)
public void afterThrowing(Exception ex){
System.out.println(ex.getMessage());
}

环绕通知(@Around) — 需要切面方法携带ProceedingJoinPoion参数,类似于一个完整的动态代理,环绕通知必须要有一个返回值,是目标方法的返
回值。

@Around(“execution(* com.itnba..*(..))”)
public object aroundAdvice( ProceedingJoinPoint pjp){

object obj = null;
try{
//做前置通知
obj = pjp.proceed();
//做返回通知
}
catch(Exception ex){
//做异常通知
}
//做后置通知
return obj;
}

 

添加日志:

切面方法可以加入参数(JoinPoint) joinPost.getSignature().getXXX()获得相关方法信息
切面方法中可以加入Object参数,用来获得目标方法的返回值(只对返回通知起作用)

 

来自:https://www.cnblogs.com/hq233/p/6637488.html

spring特点-AOP

Spring AOP
1.1 面向切面编程基础
通常,系统由很多组件组成,每个组件负责一部分功能,然而,这些组件也经常带有一些除了 核心功能之外的附带功能 。系统服务如日志、事务管理和安全经常融入到一些其他功能模块中。这些系统服务通常叫做交叉业务,这是因为它们总是分布在系统的很多组件中。通过将这些业 务分布在多个组件中,给我们的代码引入了双重复杂性。
(1) 实现系统级业务的代码在多个组件中复制。这意味着如果你要改变这些业务逻辑,你就必须到各个模块去修改。就算把这些业务抽象成一个独立模块,其它模块只是调用它的一个方法,但是这个方法调用也还是分布在很多地方。
(2) 组件会因为那些与自己核心业务无关的代码变得杂乱。一个向地址录中添加条目的方法应该只关心如何添加地址,而不是关心它是不是安全或支持事务的。
此时,我们该怎么办呢?这正是AOP用得着的地方。AOP帮助我们将这些服务模块化,并把它们声明式地应用在需要它们的地方,使得这些组件更加专注于自身业务,完全不知道其它涉及到的系统服务。
这里的概念切面,就是我们要实现的交叉功能,是应用系统模块化的一个方面或领域。切面的 最常见例子就是日志记录。日志记录在系统中到处需要用到,利用继承来重用日志模块是不合适的,这样,就可以创建一个日志记录切面,并且使用AOP在系统中 应用。下图展示了切面应用方式

图表 1 应用切面
其中,通知Advice是切面的实际实现。连接点Joinpoint是应用程序执行过程 中插入切面的地点,这个地点可以是方法调用,异常抛出,甚至可以是要修改的字段,切面代码在这些地方插入到你的应用流程中,添加新的行为。切入点 Pointcut定义了Advice应该应用在那些连接点,通常通过指定类名和方法名,或者匹配类名和方法名式样的正则表达式来指定切入点。
1.2 AOP在Spring中的实现
基于AOP,业界 存在各种各样的AOP实现,比如,JBoss AOP、Spring AOP、ASP ectJ、 Aspect Werkz等。各自实现的功能也不一样。AOP实现的强弱在很大程度上取决于连接点模型。目前,Spring只支持方法级的连接点。这和一些其他AOP框 架不一样,如AspectJ和JBoss,它们还提供了属性接入点,这样可以防止你创建特别细致的通知,如对更新对象属性值进行拦截。然而,由于 Spring关注于提供一个实现J2EE 服务的框架,所以方法拦截可以满足大部分要求,而且Spring的观点是属性拦截破坏了封装,让Advice触发在属性值改变而不是方法调用上无疑是破坏了这个概念。
Spring的AOP框架的关键点如下:
(1)Spring实现了AOP联盟接口。在Spring AOP中,存在如下几种通知(Advice)类型
Before通知:在目标方法被调用前调用,涉及接口org.springFramework .aop.MethodBeforeAdvice;
After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。
(2)用Java 编写Spring通知,并在Spring的配置文件中,定义在什么地方应用通知的切入点。
(3)Spring的运行时通知对象。代理Bean只有在第一次被应用系统需要的时候才 被创建。如果你使用的是ApplicationContext,代理对象在BeanFactory载入所有Bean的时候被创建。Spring有两种代理 创建方式。如果目标对象实现了一个或多个接口暴露的方法,Spring将使用JDK 的 java.lang.reflect.Proxy类创建代理。这个类让Spring动态产生一个新的类,它实现所需的接口,织入了通知,并且代理对目标对 象的所有请求。如果目标对象没有实现任何接口,Spring使用CGLIB库生成目标对象的子类。在创建这个子类的时候,Spring将通知织入,并且将 对目标对象的调用委托给这个子类。此时,需要将Spring发行包lib/cglib目录下的JAR文件发布到应用系统中。
1.3 Spring AOP的优势
借助于Spring AOP,Spring IoC能够很方便的使用到非常健壮、灵活的企业级服务,是因为Spring AOP能够提供如下几方面的优势:
(1)允许开发者使用声明式企业服务,比如事务服务、安全性服务;EJB 开发者都知道,EJB组件能够使用J2EE容器提供的声明式服务,但是这些服务要借助于EJB容器,而Spring AOP却不需要EJB容器,借助于Spring的事务抽象框架就可以这些服务。
(2)开发者可以开发满足业务需求的自定义切面;
(3)开发Spring AOP Advice很方便。因为这些AOP Advice仅是POJO类,借助于Spring提供的ProxyFactoryBean,能够快速的搭建Spring AOP Advice。