资料:https://gitee.com/chuanqi1995/java
配置
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-spring</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>java-spring-1.0</artifactId>
<packaging>war</packaging>
<properties>
<!-- dependency versions -->
<servlet.api.version>2.4</servlet.api.version>
</properties>
<dependencies>
<!-- requied start -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.api.version}</version>
<scope>provided</scope>
</dependency>
<!-- requied end -->
</dependencies>
<build>
<finalName>${artifactId}</finalName>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>${basedir}/src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
<exclude>**/*.class</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<verbose/>
<bootclasspath>${java.home}/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<outputDirectory>${basedir}/target/classes</outputDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<webDefaultXml>src/main/resources/webdefault.xml</webDefaultXml>
<contextPath>/</contextPath>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
</connector>
</connectors>
<scanIntervalSeconds>0</scanIntervalSeconds>
<scanTargetPatterns>
<scanTargetPattern>
<directory>src/main/webapp</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</scanTargetPattern>
</scanTargetPatterns>
<systemProperties>
<systemProperty>
<name>
javax.xml.parsers.DocumentBuilderFactory
</name>
<value>
com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
</value>
</systemProperty>
<systemProperty>
<name>
javax.xml.parsers.SAXParserFactory
</name>
<value>
com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
</value>
</systemProperty>
<systemProperty>
<name>
javax.xml.transform.TransformerFactory
</name>
<value>
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
</value>
</systemProperty>
<systemProperty>
<name>org.eclipse.jetty.util.URI.charset</name>
<value>UTF-8</value>
</systemProperty>
</systemProperties>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
<webResources>
<resource>
<!-- this is relative to the pom.xml directory -->
<directory>src/main/resources/</directory>
<targetPath>WEB-INF/classes</targetPath>
<includes>
<include>**/*.*</include>
</includes>
<!-- <excludes>
<exclude>**/local</exclude>
<exclude>**/test</exclude>
<exclude>**/product</exclude>
</excludes> -->
<filtering>true</filtering>
</resource>
<resource>
<!-- this is relative to the pom.xml directory -->
<directory>src/main/resources</directory>
<targetPath>WEB-INF/classes</targetPath>
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.zeroturnaround</groupId>
<artifactId>javarebel-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-rebel-xml</id>
<phase>process-resources</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<version>1.0.5</version>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.zeroturnaround
</groupId>
<artifactId>
javarebel-maven-plugin
</artifactId>
<versionRange>
[1.0.5,)
</versionRange>
<goals>
<goal>generate</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore></ignore>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
webdefault.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ===================================================================== -->
<!-- This file contains the default descriptor for web applications. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The intent of this descriptor is to include jetty specific or common -->
<!-- configuration for all webapps. If a context has a webdefault.xml -->
<!-- descriptor, it is applied before the contexts own web.xml file -->
<!-- -->
<!-- A context may be assigned a default descriptor by: -->
<!-- + Calling WebApplicationContext.setDefaultsDescriptor -->
<!-- + Passed an arg to addWebApplications -->
<!-- -->
<!-- This file is used both as the resource within the jetty.jar (which is -->
<!-- used as the default if no explicit defaults descriptor is set) and it -->
<!-- is copied to the etc directory of the Jetty distro and explicitly -->
<!-- by the jetty.xml file. -->
<!-- -->
<!-- ===================================================================== -->
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
metadata-complete="true"
version="2.5">
<description>
Default web.xml file.
This file is applied to a Web application before it's own WEB_INF/web.xml file
</description>
<!-- ==================================================================== -->
<!-- Context params to control Session Cookies -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<context-param>
<param-name>org.mortbay.jetty.servlet.SessionDomain</param-name>
<param-value>127.0.0.1</param-value>
</context-param>
<context-param>
<param-name>org.mortbay.jetty.servlet.SessionPath</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>org.mortbay.jetty.servlet.MaxAge</param-name>
<param-value>-1</param-value>
</context-param>
-->
<context-param>
<param-name>org.mortbay.jetty.webapp.NoTLDJarPattern</param-name>
<param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value>
</context-param>
<!-- ==================================================================== -->
<!-- The default servlet. -->
<!-- This servlet, normally mapped to /, provides the handling for static -->
<!-- content, OPTIONS and TRACE methods for the context. -->
<!-- The following initParameters are supported: -->
<!-- -->
<!-- acceptRanges If true, range requests and responses are -->
<!-- supported -->
<!-- -->
<!-- dirAllowed If true, directory listings are returned if no -->
<!-- welcome file is found. Else 403 Forbidden. -->
<!-- -->
<!-- welcomeServlets If true, attempt to dispatch to welcome files -->
<!-- that are servlets, if no matching static -->
<!-- resources can be found. -->
<!-- -->
<!-- redirectWelcome If true, redirect welcome file requests -->
<!-- else use request dispatcher forwards -->
<!-- -->
<!-- gzip If set to true, then static content will be served-->
<!-- as gzip content encoded if a matching resource is -->
<!-- found ending with ".gz" -->
<!-- -->
<!-- resoureBase Can be set to replace the context resource base -->
<!-- -->
<!-- relativeResourceBase -->
<!-- Set with a pathname relative to the base of the -->
<!-- servlet context root. Useful for only serving -->
<!-- static content from only specific subdirectories. -->
<!-- -->
<!-- useFileMappedBuffer -->
<!-- If set to true (the default), a memory mapped -->
<!-- file buffer will be used to serve static content -->
<!-- when using an NIO connector. Setting this value -->
<!-- to false means that a direct buffer will be used -->
<!-- instead. If you are having trouble with Windows -->
<!-- file locking, set this to false. -->
<!-- -->
<!-- cacheControl If set, all static content will have this value -->
<!-- set as the cache-control header. -->
<!-- -->
<!-- maxCacheSize Maximum size of the static resource cache -->
<!-- -->
<!-- maxCachedFileSize Maximum size of any single file in the cache -->
<!-- -->
<!-- maxCachedFiles Maximum number of files in the cache -->
<!-- -->
<!-- cacheType "nio", "bio" or "both" to determine the type(s) -->
<!-- of resource cache. A bio cached buffer may be used-->
<!-- by nio but is not as efficient as a nio buffer. -->
<!-- An nio cached buffer may not be used by bio. -->
<!-- -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>acceptRanges</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>dirAllowed</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>welcomeServlets</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>redirectWelcome</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>maxCacheSize</param-name>
<param-value>256000000</param-value>
</init-param>
<init-param>
<param-name>maxCachedFileSize</param-name>
<param-value>10000000</param-value>
</init-param>
<init-param>
<param-name>maxCachedFiles</param-name>
<param-value>1000</param-value>
</init-param>
<init-param>
<param-name>cacheType</param-name>
<param-value>both</param-value>
</init-param>
<init-param>
<param-name>gzip</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>useFileMappedBuffer</param-name>
<param-value>false</param-value>
</init-param>
<!--
<init-param>
<param-name>cacheControl</param-name>
<param-value>max-age=3600,public</param-value>
</init-param>
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!-- ==================================================================== -->
<!-- JSP Servlet -->
<!-- This is the jasper JSP servlet from the jakarta project -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The JSP page compiler and execution servlet, which is the mechanism -->
<!-- used by Glassfish to support JSP pages. Traditionally, this servlet -->
<!-- is mapped to URL patterh "*.jsp". This servlet supports the -->
<!-- following initialization parameters (default values are in square -->
<!-- brackets): -->
<!-- -->
<!-- checkInterval If development is false and reloading is true, -->
<!-- background compiles are enabled. checkInterval -->
<!-- is the time in seconds between checks to see -->
<!-- if a JSP page needs to be recompiled. [300] -->
<!-- -->
<!-- compiler Which compiler Ant should use to compile JSP -->
<!-- pages. See the Ant documenation for more -->
<!-- information. [javac] -->
<!-- -->
<!-- classdebuginfo Should the class file be compiled with -->
<!-- debugging information? [true] -->
<!-- -->
<!-- classpath What class path should I use while compiling -->
<!-- generated servlets? [Created dynamically -->
<!-- based on the current web application] -->
<!-- Set to ? to make the container explicitly set -->
<!-- this parameter. -->
<!-- -->
<!-- development Is Jasper used in development mode (will check -->
<!-- for JSP modification on every access)? [true] -->
<!-- -->
<!-- enablePooling Determines whether tag handler pooling is -->
<!-- enabled [true] -->
<!-- -->
<!-- fork Tell Ant to fork compiles of JSP pages so that -->
<!-- a separate JVM is used for JSP page compiles -->
<!-- from the one Tomcat is running in. [true] -->
<!-- -->
<!-- ieClassId The class-id value to be sent to Internet -->
<!-- Explorer when using <jsp:plugin> tags. -->
<!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
<!-- -->
<!-- javaEncoding Java file encoding to use for generating java -->
<!-- source files. [UTF-8] -->
<!-- -->
<!-- keepgenerated Should we keep the generated Java source code -->
<!-- for each page instead of deleting it? [true] -->
<!-- -->
<!-- logVerbosityLevel The level of detailed messages to be produced -->
<!-- by this servlet. Increasing levels cause the -->
<!-- generation of more messages. Valid values are -->
<!-- FATAL, ERROR, WARNING, INFORMATION, and DEBUG. -->
<!-- [WARNING] -->
<!-- -->
<!-- mappedfile Should we generate static content with one -->
<!-- print statement per input line, to ease -->
<!-- debugging? [false] -->
<!-- -->
<!-- -->
<!-- reloading Should Jasper check for modified JSPs? [true] -->
<!-- -->
<!-- suppressSmap Should the generation of SMAP info for JSR45 -->
<!-- debugging be suppressed? [false] -->
<!-- -->
<!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
<!-- dumped to a file? [false] -->
<!-- False if suppressSmap is true -->
<!-- -->
<!-- scratchdir What scratch directory should we use when -->
<!-- compiling JSP pages? [default work directory -->
<!-- for the current web application] -->
<!-- -->
<!-- tagpoolMaxSize The maximum tag handler pool size [5] -->
<!-- -->
<!-- xpoweredBy Determines whether X-Powered-By response -->
<!-- header is added by generated servlet [false] -->
<!-- -->
<!-- If you wish to use Jikes to compile JSP pages: -->
<!-- Set the init parameter "compiler" to "jikes". Define -->
<!-- the property "-Dbuild.compiler.emacs=true" when starting Jetty -->
<!-- to cause Jikes to emit error messages in a format compatible with -->
<!-- Jasper. -->
<!-- If you get an error reporting that jikes can't use UTF-8 encoding, -->
<!-- try setting the init parameter "javaEncoding" to "ISO-8859-1". -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet id="jsp">
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<!--
<init-param>
<param-name>classpath</param-name>
<param-value>?</param-value>
</init-param>
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspf</url-pattern>
<url-pattern>*.jspx</url-pattern>
<url-pattern>*.xsp</url-pattern>
<url-pattern>*.JSP</url-pattern>
<url-pattern>*.JSPF</url-pattern>
<url-pattern>*.JSPX</url-pattern>
<url-pattern>*.XSP</url-pattern>
</servlet-mapping>
<!-- ==================================================================== -->
<!-- Dynamic Servlet Invoker. -->
<!-- This servlet invokes anonymous servlets that have not been defined -->
<!-- in the web.xml or by other means. The first element of the pathInfo -->
<!-- of a request passed to the envoker is treated as a servlet name for -->
<!-- an existing servlet, or as a class name of a new servlet. -->
<!-- This servlet is normally mapped to /servlet/* -->
<!-- This servlet support the following initParams: -->
<!-- -->
<!-- nonContextServlets If false, the invoker can only load -->
<!-- servlets from the contexts classloader. -->
<!-- This is false by default and setting this -->
<!-- to true may have security implications. -->
<!-- -->
<!-- verbose If true, log dynamic loads -->
<!-- -->
<!-- * All other parameters are copied to the -->
<!-- each dynamic servlet as init parameters -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Uncomment for dynamic invocation
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>org.mortbay.jetty.servlet.Invoker</servlet-class>
<init-param>
<param-name>verbose</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>nonContextServlets</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>dynamicParam</param-name>
<param-value>anyValue</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
-->
<!-- ==================================================================== -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- ==================================================================== -->
<!-- Default MIME mappings -->
<!-- The default MIME mappings are provided by the mime.properties -->
<!-- resource in the org.mortbay.jetty.jar file. Additional or modified -->
<!-- mappings may be specified here -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<mime-mapping>
<extension>mysuffix</extension>
<mime-type>mymime/type</mime-type>
</mime-mapping>
-->
<!-- ==================================================================== -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- ==================================================================== -->
<locale-encoding-mapping-list>
<locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>
</locale-encoding-mapping-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>Disable TRACE</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
</web-app>
application.properties
#托管的类扫描包路径#
scanPackage=com.spring.aop.v1.demo
#切面表达式expression#
pointCut=public .* com.spring.aop.v1.demo.service..*Service..*(.*)
#切面类
aspectClass=com.spring.aop.v1.demo.aspect.LogAspect
#前置通知回调方法
aspectBefore=before
#后置通知回调方法
aspectAfter=after
#异常通知回调方法
aspectAfterThrow=afterThrowing
#异常类型捕获
aspectAfterThrowingName=java.lang.Exception
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>sucqmvc</servlet-name>
<servlet-class>com.mvcframework.v2.servlet.SDispatchServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sucqmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
业务代码
controller
package com.demo.spring.action;
import com.demo.spring.service.IDemoService;
import com.mvcframework.annotation.SAutowired;
import com.mvcframework.annotation.SController;
import com.mvcframework.annotation.SRequestMapping;
import com.mvcframework.annotation.SRequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//虽然,用法一样,但是没有功能
@SController
@SRequestMapping("/demo")
public class DemoAction {
@SAutowired
private IDemoService demoService;
@SRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp,
@SRequestParam("name") String name){
String result = demoService.get(name);
// String result = "My name is " + name;
try {
resp.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@SRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp,
@SRequestParam("a") Integer a, @SRequestParam("b") Integer b){
try {
resp.getWriter().write(a + "+" + b + "=" + (a + b));
} catch (IOException e) {
e.printStackTrace();
}
}
@SRequestMapping("/sub")
public void add(HttpServletRequest req, HttpServletResponse resp,
@SRequestParam("a") Double a, @SRequestParam("b") Double b){
try {
resp.getWriter().write(a + "-" + b + "=" + (a - b));
} catch (IOException e) {
e.printStackTrace();
}
}
@SRequestMapping("/remove")
public String remove(@SRequestParam("id") Integer id){
return "" + id;
}
}
package com.demo.spring.action;
//没加注解,控制权不反转,自己管自己
public class TestAction {
}
package com.demo.spring.action;
import com.demo.spring.service.IDemoService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//没加注解,控制权不反转,自己管自己
public class TwoAction {
private IDemoService demoService;
public void edit(HttpServletRequest req,HttpServletResponse resp,
String name){
String result = demoService.get(name);
try {
resp.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
entity
package com.demo.spring.entity;
public class Test {
public static void main(String[] args) {
String simpleName = Test.class.getSimpleName();
System.out.println(simpleName);
}
}
service
package com.demo.spring.service;
public interface IDemoService {
String get(String name);
}
package com.demo.spring.service.impl;
import com.demo.spring.service.IDemoService;
import com.mvcframework.annotation.SService;
/**
* 核心业务逻辑
*/
@SService
public class DemoService implements IDemoService {
public String get(String name) {
return "My name is " + name + ",from service.";
}
}
aspect
package com.spring.aop.v1.demo.aspect;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogAspect {
//在调用一个方法之前,执行before方法
public void before(){
//这个方法中的逻辑,是由我们自己写的
log.info("Invoker Before Method!!!");
}
//在调用一个方法之后,执行after方法
public void after(){
log.info("Invoker After Method!!!");
}
public void afterThrowing(){
log.info("出现异常");
}
}
spring代码
注解
package com.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SAutowired {
String value() default "";
}
package com.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SController {
String value() default "";
}
package com.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SRequestMapping {
String value() default "";
}
package com.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SRequestParam {
String value() default "";
}
package com.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SService {
String value() default "";
}
servlet
package com.spring.framework.webmvc.servlet;
import com.spring.framework.annotation.*;
import com.spring.framework.context.SApplicationContext;
import com.spring.framework.support.SDefaultListableBeanFactory;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
public class SDispatchServlet extends HttpServlet {
/**
* Spring设计思路
* 一、知识点
* 1、Spring中的bean默认是单例的
* 2、Spring中的bean默认是非延时加载的
* 1、非延时加载:在Spring启动之后,对象就创建在Spring的IOC容器里了
* 2、延时加载:在用户使用ApplicationContext.getBean()时才加载bean到IOC容器
*
* 二、步骤
* 在调用Servlet的init方法的时候
* 1、初始化AppliactionContext
* 2、加载配置文件
* 1、读取到配置文件(BeanDefinitionReader)
* 2、扫描到相关的类,保存起来(BeanDefinitionReader)
* 3、解析配置文件,将所有配置文件封装起来(BeanDefinition)
* 4、将所有封装起来的BeanDefinition缓存起来(SDefaultListableBeanFactory)
* 5、加载非延时加载的所有bean(getBean())
*
* 在调用ApplicationContext.getBean()方法时
* 6、先根据beanName拿到BeanDefinition配置信息
* 7、反射实例化对象
* 8、将返回的bean封装成(BeanWrapper)
* 9、DI依赖注入
* 10、存入一级缓存
*/
// ==================Spring-2.0-ioc开始==================
SApplicationContext applicationContext;
// ==================Spring-2.0-ioc结束==================
private Map<String,Method> handlerMapping = new HashMap<String, Method>();
@Override
public void init(ServletConfig config) throws ServletException {
// 1、初始化AppliactionContext
applicationContext = new SApplicationContext(config.getInitParameter("contextConfigLocation").replaceAll("classpath:", ""));
//==============MVC部分==============
//2、初始化HandlerMapping
doInitHandlerMapping();
System.out.println("S Spring framework is init.");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//6、委派,根据URL去找到一个对应的Method并通过response返回
try {
doDispatch(req,resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath,"").replaceAll("/+","/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404 Not Found!!!");
return;
}
Map<String,String[]> params = req.getParameterMap();
Method method = this.handlerMapping.get(url);
//获取形参列表
Class<?> [] parameterTypes = method.getParameterTypes();
Object [] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class paramterType = parameterTypes[i];
if(paramterType == HttpServletRequest.class){
paramValues[i] = req;
}else if(paramterType == HttpServletResponse.class){
paramValues[i] = resp;
}else if(paramterType == String.class){
//通过运行时的状态去拿到你
Annotation[] [] pa = method.getParameterAnnotations();
for (int j = 0; j < pa.length ; j ++) {
for(Annotation a : pa[i]){
if(a instanceof SRequestParam){
String paramName = ((SRequestParam) a).value();
if(!"".equals(paramName.trim())){
String value = Arrays.toString(params.get(paramName))
.replaceAll("\\[|\\]","")
.replaceAll("\\s+",",");
paramValues[i] = value;
}
}
}
}
}
}
//暂时硬编码
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//赋值实参列表
method.invoke(applicationContext.getBean(beanName),paramValues);
}
private void doInitHandlerMapping() {
if(this.applicationContext.getBeanDefinitionCount() == 0){
return;
}
for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
Object instance = applicationContext.getBean(beanName);
Class<?> clazz = instance.getClass();
if(!clazz.isAnnotationPresent(SController.class)){ continue; }
//相当于提取 class上配置的url
String baseUrl = "";
if(clazz.isAnnotationPresent(SRequestMapping.class)){
SRequestMapping requestMapping = clazz.getAnnotation(SRequestMapping.class);
baseUrl = requestMapping.value();
}
//只获取public的方法
for (Method method : clazz.getMethods()) {
if(!method.isAnnotationPresent(SRequestMapping.class)){continue;}
//提取每个方法上面配置的url
SRequestMapping requestMapping = method.getAnnotation(SRequestMapping.class);
// //demo//query
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+","/");
handlerMapping.put(url,method);
System.out.println("Mapped : " + url + "," + method);
}
}
}
//自己写,自己用
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
// if(chars[0] > )
chars[0] += 32;
return String.valueOf(chars);
}
}
context
package com.spring.aop.v1.spring.framework.context;
import com.alibaba.fastjson.JSONObject;
import com.spring.aop.v1.spring.framework.annotation.SAutowired;
import com.spring.aop.v1.spring.framework.annotation.SController;
import com.spring.aop.v1.spring.framework.annotation.SService;
import com.spring.aop.v1.spring.framework.aop.SJdkDynamicAopProxy;
import com.spring.aop.v1.spring.framework.aop.config.SAopConfig;
import com.spring.aop.v1.spring.framework.aop.support.SAdvisedSupport;
import com.spring.aop.v1.spring.framework.beans.SBeanWrapper;
import com.spring.aop.v1.spring.framework.beans.config.SBeanDefinition;
import com.spring.aop.v1.spring.framework.support.SBeanDefinitionReader;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class SApplicationContext {
// 加载配置文件的工具类
SBeanDefinitionReader reader;
// BeanDefinition缓存
Map<String, SBeanDefinition> beanDefinitionMap = new HashMap<String, SBeanDefinition>();
//循环依赖的标识,当前正在创建的BeanName,Mark一下
private Set<String> singletonsCurrentlyInCreation = new HashSet<String>();
//一级缓存:保存成熟的Bean
private Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
//二级缓存:保存早期的Bean
private Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();
//三级缓存
private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
public SApplicationContext(String contextConfigLocation) {
try {
// 3、加载配置文件,把扫描到的类,保存起来,保存成BeanClassName(com.xxx.xxController)
reader = new SBeanDefinitionReader(contextConfigLocation);
// 4、将保存起来的BeanClass封装成BeanDefinition
List<SBeanDefinition> beanDefinitionList = reader.doLoadBeanDefinition();
// 5、将封装起来的BeanDefinition缓存起来,缓存成BeanDefinitionMap<BeanName, BeanDefinition>
doLoadBeanDefinitionMap(beanDefinitionList);
// 6、加载非延时加载的Bean(调用Application.getBean(beanName))
doLoadInstance();
}catch (Exception e){
e.printStackTrace();
}
}
private void doLoadInstance() {
for (Map.Entry<String, SBeanDefinition> entry : beanDefinitionMap.entrySet()) {
if(!entry.getValue().isLazyInit()){
getBean(entry.getKey());
}
}
}
private void doLoadBeanDefinitionMap(List<SBeanDefinition> beanDefinitionList) throws Exception {
for (SBeanDefinition beanDefinition : beanDefinitionList) {
if(beanDefinitionMap.containsKey(beanDefinition.getBeanName())){
throw new Exception("The " + beanDefinition.getBeanName() + "is exists!!!");
}
beanDefinitionMap.put(beanDefinition.getBeanName(), beanDefinition);
System.out.println("封装的beanDefinitionMap:" + beanDefinition.getBeanName());
}
}
public Object getBean(String beanName){
// 7、根据BeanName获取BeanDefinition信息
SBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
Object singleton = getSingleton(beanName, beanDefinition);
if (singleton != null) {
return singleton;
}
//标记bean正在创建
if (!singletonsCurrentlyInCreation.contains(beanName)) {
// 把A放入其中,代表我要去创建了
singletonsCurrentlyInCreation.add(beanName);
}
// 8、根据反射实例化对象
Object instance = instantiateBean(beanDefinition);
// 9、将实例化的对象封装成BeanWrapper
SBeanWrapper beanWrapper = new SBeanWrapper(instance);
// 10、DI依赖注入
populateBean(beanName, beanDefinition, beanWrapper);
// 11、将实例化的对象存入缓存
factoryBeanObjectCache.put(beanName, instance);
return instance;
}
private void populateBean(String beanName, SBeanDefinition beanDefinition, SBeanWrapper beanWrapper) {
Object instance = beanWrapper.getWrappedInstance();
Class<?> clazz = beanWrapper.getWrappedClass();
if (!(clazz.isAnnotationPresent(SController.class)
|| clazz.isAnnotationPresent(SService.class))) {
return;
}
//忽略字段的修饰符,不管你是 private / protected / public / default
for (Field field : clazz.getDeclaredFields()) {
if (!field.isAnnotationPresent(SAutowired.class)) {
continue;
}
SAutowired autowired = field.getAnnotation(SAutowired.class);
String autowiredBeanName = autowired.value().trim();
if ("".equals(autowiredBeanName)) {
autowiredBeanName = field.getType().getName();
}
//代码在反射面前,那就是裸奔
//强制访问,强吻
field.setAccessible(true);
try {
// if(factoryBeanObjectCache.get(autowiredBeanName) == null){
// continue;
// }
field.set(instance, getBean(autowiredBeanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private Object instantiateBean(SBeanDefinition beanDefinition) {
String beanName = beanDefinition.getBeanName();
String beanClassName = beanDefinition.getBeanClassName();
// 未来可能存在循环依赖,可能需要在三级缓存中直接拿
if (this.factoryBeanObjectCache.containsKey(beanName)) {
return this.factoryBeanObjectCache.get(beanName);
}
Object instance = null;
try {
Class<?> clazz = Class.forName(beanClassName);
instance = clazz.newInstance();
//==================AOP开始=========================
//如果满足条件,就直接返回Proxy对象
//1、加载AOP的配置文件
SAdvisedSupport config = instantionAopConfig(beanDefinition);
config.setTargetClass(clazz);
config.setTarget(instance);
//判断规则,要不要生成代理类,如果要就覆盖原生对象
//如果不要就不做任何处理,返回原生对象
if(config.pointCutMath()){
instance = new SJdkDynamicAopProxy(config).getProxy();
}
//===================AOP结束========================
factoryBeanObjectCache.put(beanName, instance);
}catch (Exception e){
e.printStackTrace();
}
return instance;
}
private Object getSingleton(String beanName, SBeanDefinition beanDefinition) {
// 虽然我们最开始从A开始加载,然后注入属性B,然后B再去创建,属性注入A
//先去一级缓存里面拿,现在是B注入属性A
Object bean = singletonObjects.get(beanName);
//如果一级缓存中没有,但是又有创建标识,说明就是循环依赖
if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
bean = earlySingletonObjects.get(beanName);
//如果二级缓存也没有,从三级缓存中拿
if (bean == null) {
bean = instantiateBean(beanDefinition);
//将创建出来的对象重新放入到二级缓存中
earlySingletonObjects.put(beanName, bean);
}
}
return bean;
}
private SAdvisedSupport instantionAopConfig(SBeanDefinition beanDefinition) {
SAopConfig config = new SAopConfig();
config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
return new SAdvisedSupport(config);
}
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
public String[] getBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[0]);
}
}
core
package com.spring.framework.core;
public interface SBeanFactory {
/**
* 根据beanName获取对象
* @param beanName
* @return
*/
Object getBean(String beanName);
/**
* 根据Class获取对象
* @param beanClass
* @return
*/
Object getBean(Class<?> beanClass);
}
beans
package com.spring.framework.beans.config;
public class SBeanDefinition {
// 是否延时加载
// 默认否(false)
public boolean isLazyInit(){
return false;
};
private String factoryBeanName; // beanName
private String beanClassName; // 原生类的全类名
public String getFactoryBeanName() {
return factoryBeanName;
}
public void setFactoryBeanName(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
}
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
}
@Override
public String toString() {
return "SBeanDefinition{" +
"factoryBeanName='" + factoryBeanName + '\'' +
", beanClassName='" + beanClassName + '\'' +
'}';
}
}
package com.spring.framework.beans;
public class SBeanWrapper {
private Object wrappedInstance;
private Class<?> wrapperClass;
public SBeanWrapper(Object instance) {
this.wrappedInstance = instance;
this.wrapperClass = instance.getClass();
}
public Object getWrapperedInstance() {
return wrappedInstance;
}
public Class<?> getWrapperClass() {
return wrapperClass;
}
}
support
package com.spring.aop.v1.spring.framework.support;
import com.alibaba.fastjson.JSONObject;
import com.spring.aop.v1.spring.framework.beans.config.SBeanDefinition;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class SBeanDefinitionReader {
Properties contextConfig = new Properties();
// com.xxx.XxController
private List<String> beanClassNameList = new ArrayList<String>();
public SBeanDefinitionReader(String contextConfigLocation) {
// 1、扫描文件
doLoadConfig(contextConfigLocation);
// 2、保存成BeanClassName
doScanner(contextConfig.getProperty("scanPackage"));
}
private void doScanner(String property) {
// property = com.demo.spring.aop.v1
// /com/demo/spring/aop/v1
URL url = this.getClass().getClassLoader().getResource("/" + property.replaceAll("\\.", "/"));
File urlFile = new File(url.getFile());
for (File file : urlFile.listFiles()) {
if(file.isDirectory()){
doScanner(property + "." + file.getName());
}else{
String className = property + "." + file.getName().replaceAll("/", ".").replaceAll(".class", "");
beanClassNameList.add(className);
System.out.println("扫描到的类:" + className);
}
}
}
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public List<SBeanDefinition> doLoadBeanDefinition() {
List<SBeanDefinition> beanDefinitionList = new ArrayList<SBeanDefinition>();
try {
for (String beanClassName : beanClassNameList) {
Class<?> clazz = Class.forName(beanClassName);
// 如果是接口不做处理
if(clazz.isInterface()){
continue;
}
// 默认类名首字母小写
SBeanDefinition beanDefinition = new SBeanDefinition();
beanDefinition.setBeanName(toLowerFirstCase(clazz.getSimpleName()));
beanDefinition.setBeanClassName(clazz.getName());
beanDefinitionList.add(beanDefinition);
System.out.println("封装的BeanDefiniaiton:" + JSONObject.toJSONString(beanDefinition));
// 如果有接口,就用实现类
for (Class<?> i : clazz.getInterfaces()) {
SBeanDefinition beanDefinitionI = new SBeanDefinition();
beanDefinitionI.setBeanName(i.getName());
beanDefinitionI.setBeanClassName(clazz.getName());
beanDefinitionList.add(beanDefinitionI);
System.out.println("封装的BeanDefiniaiton:" + JSONObject.toJSONString(beanDefinition));
}
}
}catch (Exception e){
e.printStackTrace();
}
return beanDefinitionList;
}
private String toLowerFirstCase(String beanName) {
char[] chars = beanName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
public Properties getConfig(){
return this.contextConfig;
}
}
package com.spring.framework.support;
import com.spring.framework.beans.config.SBeanDefinition;
import com.spring.framework.core.SBeanFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SDefaultListableBeanFactory implements SBeanFactory {
public Map<String, SBeanDefinition> beanDefinitionMap = new HashMap<String, SBeanDefinition>();
@Override
public Object getBean(String beanName) {
return null;
}
@Override
public Object getBean(Class<?> beanClass) {
return null;
}
public void doRegistBeanDefinition(List<SBeanDefinition> beanDefinitions) throws Exception {
for (SBeanDefinition beanDefinition : beanDefinitions) {
if(this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists!!!");
}
beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
System.out.println("缓存【SBeanDefinition】:" + beanDefinition.getFactoryBeanName());
}
}
}
aop
package com.spring.aop.v1.spring.framework.aop.config;
import lombok.Data;
@Data
public class SAopConfig {
private String pointCut;
private String aspectClass;
private String aspectBefore;
private String aspectAfter;
private String aspectAfterThrow;
private String aspectAfterThrowingName;
}
package com.spring.aop.v1.spring.framework.aop.aspect;
import lombok.Data;
import java.lang.reflect.Method;
@Data
public class SAdvice {
private Object aspect;
private Method adviceMethod;
private String throwName;
public SAdvice(Object aspect, Method adviceMethod) {
this.aspect = aspect;
this.adviceMethod = adviceMethod;
}
}
package com.spring.aop.v1.spring.framework.aop.support;
import com.spring.aop.v1.spring.framework.aop.aspect.SAdvice;
import com.spring.aop.v1.spring.framework.aop.config.SAopConfig;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 解析AOP配置的工具类
*/
public class SAdvisedSupport {
private SAopConfig config;
private Object target;
private Class targetClass;
private Pattern pointCutClassPattern;
private Map<Method,Map<String, SAdvice>> methodCache;
public SAdvisedSupport(SAopConfig config) {
this.config = config;
}
//解析配置文件的方法
private void parse() {
//把Spring的Excpress变成Java能够识别的正则表达式
String pointCut = config.getPointCut()
.replaceAll("\\.", "\\\\.")
.replaceAll("\\\\.\\*", ".*")
.replaceAll("\\(", "\\\\(")
.replaceAll("\\)", "\\\\)");
//保存专门匹配Class的正则
String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
//享元的共享池
methodCache = new HashMap<Method, Map<String, SAdvice>>();
//保存专门匹配方法的正则
Pattern pointCutPattern = Pattern.compile(pointCut);
try{
Class aspectClass = Class.forName(this.config.getAspectClass());
Map<String,Method> aspectMethods = new HashMap<String, Method>();
for (Method method : aspectClass.getMethods()) {
aspectMethods.put(method.getName(),method);
}
for (Method method : this.targetClass.getMethods()) {
String methodString = method.toString();
if(methodString.contains("throws")){
methodString = methodString.substring(0,methodString.lastIndexOf("throws")).trim();
}
Matcher matcher = pointCutPattern.matcher(methodString);
if(matcher.matches()){
Map<String, SAdvice> advices = new HashMap<String, SAdvice>();
if(!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))){
advices.put("before",new SAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectBefore())));
}
if(!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))){
advices.put("after",new SAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfter())));
}
if(!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))){
SAdvice advice = new SAdvice(aspectClass.newInstance(),aspectMethods.get(config.getAspectAfterThrow()));
advice.setThrowName(config.getAspectAfterThrowingName());
advices.put("afterThrow",advice);
}
//跟目标代理类的业务方法和Advices建立一对多个关联关系,以便在Porxy类中获得
methodCache.put(method,advices);
}
}
}catch(Exception e){
e.printStackTrace();
}
}
//根据一个目标代理类的方法,获得其对应的通知
public Map<String, SAdvice> getAdvices(Method method, Object o) throws Exception {
//享元设计模式的应用
Map<String, SAdvice> cache = methodCache.get(method);
if(null == cache){
Method m = targetClass.getMethod(method.getName(),method.getParameterTypes());
cache = methodCache.get(m);
this.methodCache.put(m,cache);
}
return cache;
}
//给ApplicationContext首先IoC中的对象初始化时调用,决定要不要生成代理类的逻辑
public boolean pointCutMath() {
return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
}
public void setTargetClass(Class<?> targetClass) {
this.targetClass = targetClass;
parse();
}
public void setTarget(Object target) {
this.target = target;
}
public Class getTargetClass() {
return targetClass;
}
public Object getTarget() {
return target;
}
}
package com.spring.aop.v1.spring.framework.aop;
import com.spring.aop.v1.spring.framework.aop.aspect.SAdvice;
import com.spring.aop.v1.spring.framework.aop.support.SAdvisedSupport;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
public class SJdkDynamicAopProxy implements InvocationHandler {
private SAdvisedSupport config;
public SJdkDynamicAopProxy(SAdvisedSupport config) {
this.config = config;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Map<String, SAdvice> advices = config.getAdvices(method,null);
Object returnValue;
try {
invokeAdivce(advices.get("before"));
returnValue = method.invoke(this.config.getTarget(),args);
invokeAdivce(advices.get("after"));
}catch (Exception e){
invokeAdivce(advices.get("afterThrow"));
throw e;
}
return returnValue;
}
private void invokeAdivce(SAdvice advice) {
try {
advice.getAdviceMethod().invoke(advice.getAspect());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.config.getTargetClass().getInterfaces(),this);
}
}