SSM:
即是Spring SpringMVC Mybatis的开头字母
Spring
SSM里的Spring其实主要常用的是IoC和Aop,还有Test
- Spring默认的xml配置文件为 applicationContext.xml
Spring IoC
中文译名:控制反转/依赖注入,简单来说,即是原来的new xxx();
用spring中content的bean代替,实现在构造函数的时候自动注入参数,有三种配置方式:基于xml,基于注解,基于java类,有三种注入方式:构造注入,set注入,工厂注入(不常用).
基于xml的配置
1 | <!--标识符(identifiers)即bean的身份标识--> |
基于注解的配置
1 | //自动生成Bean. 与@Repository(用于DAO),@Service(用于Service),@Controller(用于Controller)作用相同 |
另外还要在Spring的xml配置文件中配置以使注解生效
1 | <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/> |
基于java配置类的配置
即是将xml配置文件里的配置提出来,放到一个java文件里,成为一个配置类.这样的好处即是不需要xml文件了,实现去xml化.
1 | //btw spring boot里的@SpringBootApplication一条等于@Configuration,@EnableAutoConfiguration,@ComponentScan三条 |
btw: Scope标识此bean的作用域,常在多线程问题中使用.
什么时候该在xml里定义bean什么时候该用扫描
在xml里定义bean
- 非常严谨需要一个个定义
- 外部引入,无法修改的类
- 我就是享受那种一个个定义的快感的时候
不需要定义bean
- 怕在xml中配置的时候手滑
- 懒
Spring AOP
Aspect-Oriented Programming(AOP,面向切面的程序设计)是对于Object-oriented programming(OOP,面向对象程序设计)的一个改进.
我对于AOP的理解: 即hook住了一个类(要代理的类)中的函数,使得我们可以在函数的之前之后跑一段其他代码.SpringAOP使得获取到的对象(Bean)为代理后的而不是原有的.
btw:当代理类中的函数互相调用时,SpringAOP不会起作用.
通过继承接口实现的AOP
此处的接口指的是SpringAOP拦截器的接口,具体来说即继承org.springframework.aop下的函数:
- MethodBeforeAdvice.before()
- AfterReturningAdvice.afterReturning()
- MethodInterceptor.invoke()
- ThrowsAdvice的AfterThrowing()(ThrowAdvice并没有此接口,但是继承后的函数名依然要是AfterThrowing,原因)
1 | public class xxxxHelper_or_u_want implements MethodBeforeAdvice,MethodInterceptor,AfterReturningAdvice { |
Spring的xml配置文件
1 | <bean id="xxxxImpl" class="com.test.xxxxImpl"> |
通过xml来配置(AspectJ)
此种方法相比较第一种方法的最大区别即是不用继承接口.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class xxxxHelper_or_u_want {
public void before_or_dsoiafd() {
System.out.println("在MethodInterceptor之前执行");
}
public Object around_or_aisfad(ProceedingJoinPoint invocation) throws Throwable{
System.out.println("参数" + invocation.getArguments()[0] + "--执行函数" +
invocation.getStaticPart() + "--执行类" +
invocation.getThis().getClass() + "--执行函数" +
invocation.getMethod().getName());
Object ret=invocation.proceed();//真正执行方法,也就是说你可以搞事:D
System.out.println("环绕增强调用结束");
return ret;//可以修改函数返回值
}
public void after_or_sajdoip() {
System.out.println("在MethodInterceptor之后执行");
}
Spring的xml配置文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<bean id="xxxxHelper" class="com.test.xxxxHelper_or_u_want">
<aop:config>
<aop:aspect ref="xxxxHelper">
<!-- 定义切点 -->
<!-- expression内的是AspectJ表达式 -->
<aop:pointcut id="init_cutpoint" expression="execution(public com.test.xxxx.fun_name(..))"/>
<!-- 前置通知 -->
<aop:before pointcut-ref="init_cutpoint" method="before_or_dsoiafd"/>
<!-- 后置通知 -->
<aop:after pointcut-ref="init_cutpoint" method="after_or_sajdoip"/>
<!-- 环绕通知 -->
<!--也可以直接写AspectJ表达式-->
<aop:around pointcut="execution(* com.test.xxxx.fun_name(..))" method="around_or_aisfad"/>
</aop:aspect>
</aop:config>
通过注解来配置(AspectJ)
1 | //使得Spring可以自动扫描 |
Spring的xml配置文件1
2
3
4<aop:aspectj-autoproxy /><!-- 扫描@Aspect -->
<!-- 更懒一些还可以选择@Component :D -->
<bean id="xxxxImpl" class="com.test.xxxxImpl">
<bean id="xxxxHelper" class="com.test.xxxxHelper_or_u_want">
AspectJ表达式
AspectJ表达式通配符
符号 | 作用 |
---|---|
*** | 匹配任何数量的字符 |
.. | 匹配任何数量的任何数量的字符,换句话说即匹配任何数量的*** |
+ | 跟在类后面,表示此类的子类(不包含其本身) |
- 对于简单的例子
- execution(public com.test.xxxxImpl.fun_name(..)))
参数 | 作用 |
---|---|
public | public类型 |
com.test.xxxxImpl | 包名 |
. | 连接包名与函数(方法)名 |
fun_name | 函数(方法)名 |
(..) | 所有参数 |
那么可以推出
表达式 | 作用 |
---|---|
execution(public com.test.xxxxImpl.fun_name(..))) | 任意参数的public的com.test.xxxImple.fun_name函数 |
execution(public com.test.xxxx+.fun_name(..))) | 任意参数的public的实现了com.test.xxx的fun_name函数的函数 |
execution(public com.test.xxxx+.(..)))* | 任意参数的public的实现了com.test.xxx的任何函数的函数 |
execution( com.test.xxxx+.(..))) | 任意参数的任意类型的实现了com.test.xxx的任何函数的函数 |
execution(public com.test.(..)))* | 任意参数的public的com.test包下的任何类(com.test.*)的任何函数 |
execution(public com.test..(..)))* | 任意参数的public的com.test./com.test..*的任何函数 |
Test
配置log4j后,使用context.getBean()获取Bean,调用类中的函数
SpringMVC
SpringMvc(全称Spring Web MVC,位于spring-webmvc.jar)通过一个叫做DispatcherServlet的Servlet来获取 ModelAndView 来传递内容,并在View
(一般是.jsp)中将Model
渲染出来从而生成web页面.
btw: Spring5中引入了一个新的web框架Spring-webflux
SpringMVC与Servlet容器(如Tomcat)的配置
有多个Servlet的配置
- 需要配置[Servlet-name]-serlvet.xml
在web.xml中配置DispatcherServlet即可,就如其名,它是个Servlet,他将url-pattern中的请求转发到关联的的serlet-name中,这样的好处即是可以将多个Servlet的Bean分离(或者说是每个Servlet的WebApplicationContext),互不影响.
在web.xml下进行如此配置
1 | <web-app> |
即配置/example/**下的所有请求都被名叫servlet_name的servlet处理(具体配置文件为/WEB-INF/servlet_name-servlet.xml*)
当然也有用java配置类的方法,只需要继承org.springframework.web下的WebApplicationInitializer接口即可1
2
3
4
5
6
7
8
9
10
public class MyWebApplicationInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) {
ServletRegistration.Dynamic registration = container.addServlet("servlet_name",
new DispatcherServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/example/*");
}
}
仅有单个Servlet的配置(特殊)
- 仅需要配置applicationContext.xml
如果仅有一个Servlet,那么可以将将所有请求拦截,然后交由Spring的ApplicationContext(或者说Root的WebApplicationContext)处理.
在web.xml下进行如下配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<web-app>
<!-- 可以折么修改applicationContext.xml的名字 -->
<!-- <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param> -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class
>
<!-- 也可以这么修改applicationContext.xml的名字 -->
<!-- <init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param> -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
btw: 除了ContextLoaderListener,还有ContextLoaderServlet的实现
SpringMVC的Controller
基于注解的配置
IndexController.java(在com.test)
1 | // 类似于@Component但是是由springmvc解析 |
index.jsp(在/WEB-INF/views/下)
1 | <p>${message}</p> |
servlet_name-servlet.xml
1 | <!-- 拦截所有请求时,需要此配置来使@Controller获得比<mvc:default-servlet-handler/> 更高的优先级 --> |
此时访问http://localhost:8080/example/即可看到由IndexController传递message给/WEB-INF/views/index.jsp并显示的页面
- Controller中的model的区别
Model | 特点 | 使用 | 返回 |
---|---|---|---|
ModelMap | 由Spring自动创建 | 作为函数方法的参数接受,并使用addAttribute传递参数 | 返回String |
ModelAndView | 自己手动创建 | 使用new来创建,并使用addObject传递参数 | 返回Model |
- 返回字符串的关键字
String | 作用 |
---|---|
forward: | 服务器转发 |
redirect: | 客户端转发 |
基于xml的配置
java继承并重写Controller(org.springframework.web.servlet.mvc)的handleRequest函数即可1
2
3
4
5
6
7public class IndexController implements Controller {
public ModelAndView handleRequest(HttpServletRequest req,
HttpServletResponse res)
throws Exception {
return new ModelAndView("index");
}
index.jsp不变
servlet_name-servlet.xml1
2
3
4
5
6
7
8
9
10
11<!-- 因为id不能包括'/' -->
<!-- 而DispatcherServlet注册的BeanNameUrlHandlerMapping需要以'/'开头 -->
<!-- 所以只能设置为name别名 -->
<bean name="/" class="com.test.IndexController" />
<!-- 视图解析器,为返回的字符串添加前缀与后缀 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
基于java配置类的配置
参见Srping部分
即是@Configuration ,@ComponentScan 和@Bean 等的使用
RESTFul
RESTFul,即符合REST(Representational State Transfer) principles的系統,通常用来做api,使用json(大多数)或xml通过http的方法来传递参数
我的理解即是视任何东西为对象,用各种http方法(PUT,GET,DELETE,PATCH等)来标识作用,真正要用的时候查查github的Api看看怎么写的照猫画虎就OK 😂
SpringMVC中使用@RequestMapping,@PathVariable,@RequestBody和@ResponseBody来方便的实现RESTFul
注解 | 作用 |
---|---|
@RequestMapping | 绑定Controller到指定路径(api的) |
@PathVariable | 标识路径中的变量与函数方法参数的关系 |
@RequestBody | 用来获取传入的数据(json/xml) |
@ResponseBody | 用来直接返回数据而不是调用model |
- Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestController {
"/postJson",method = RequestMethod.POST) (value=
public String index(@RequestBody Id_And_Name ian){
StringBuffer sb=new StringBuffer("");
sb.append(ian.id);
sb.append("---->");
sb.append(ian.name)
model.addAttribute("message", sb.toString());
return "index";
}
"/getJson/{id}",method = RequestMethod.GET) (value=
public Id_And_Name index(@PathVariable int uid){
Id_And_Name ian=new Id_And_Name().get_name_by_id(uid)
return ian;
}
}
btw: RPC就是使用二进制,而不是http,实现不同系统间相互通信
Mybatis
Mybatis(ibatis)
他通过SqlSession(由SqlSessionFactory创建)来执行(Excutor())各种操作
我个人感觉折腾这个比折腾hibernate麻烦(当然是在使用Intellij IDEA的情况下)
Mybatis配置
配置环境
通过mybatis-config.xml来进行配置坏境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<configuration>
<!-- 引入.properties文件,使得可以在下面的配置中引用 -->
<!-- db.properties文件中有jdbc.password=toor等 -->
<properties resource="db.properties">
<environments default="development">
<environment id="development">
<!-- 采用jdbc的事务管理 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value=${jdbc.password} />
</dataSource>
</environment>
</environments>
</configuration>选择的数据表每条数据包含id,username,password内容
实体类User(com.test.entity)
1
2
3
4
5
6public class User{
int id;
String username;
String password;
// set,get方法略
}
配置数据库方法
仅使用xml文件配置
mybatis-config.xml
1
2
3
4
5
6<configuration>
<mappers>
<!-- 引用具体的mapper文件 -->
<mapper resource="com/test/mapper/UserMapper.xml" />
</mappers>
</configuration>被引用的UserMapper.xml文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- namespace的值习惯上设置成包名+sql映射文件名 -->
<mapper namespace="com.test.mapper.UserMapper">
<select id="selectUserById" parameterType="int" resultType="user">
select * from user where id=#{id}
<if test="name !=null">
and name=#{name}
</if>
</select>
<select id="selectUserListByName" parameterType="string" resultType="user">
select * from user where username=#{name}
</select>
<insert id="saveUser" parameterType="user">
insert into user values(#{username},#{password})
</insert>
<delete id="deleteUserById" parameterType="int">
delete from user where id=#{id}
</delete>
<update id="udpateUserById" parameterType="user">
update user set username=#{username} where id=#{id}
</update>
</mapper>
或者使用Mapper代理方式(写个接口)的方式配置
UserMapper.java
1
2
3
4public interface UserMapper {
public User selectUserById(int id);
public int deleteUserById(int id);
}mybatis-config.xml与UserMapper.xml文件不变
UserMapper.xml中的namespace等于mapper接口地址
接口中的函数方法名与xml中的statemen的id一致
输入输出的类型要一致
或者直接在Mapper接口上进行注解
UserMapper.java
1
2
3
4
5
6public interface UserMapper {
"select * from user where id=#{id}") (
public User selectUserById(int id);
"delete from user where id=#{id}") (
public int deleteUserById(@Param("id") int uid);
}UserMapper.xml
1
2
3
4
5
6<configuration>
<mappers>
<!-- 引用mapper的class -->
<mapper class="com.test.mapper.UserMappe" />
</mappers>
</configuration>
另外,使用typeAliases可设置别名从而精简书写量
1
2
3
4
5
6
7
8<configuration>
<typeAliases>
<!-- 手动 -->
<typeAlias type="com.test.entity.User" alias="user"/>
<!-- 自动,以首字母小写的非限定类名来作为它的别名-->
<package name="com.test.mapper.UserMapper"/>
</typeAliases>
</configuration>
mapper | 特点 |
---|---|
resource | xml文件 |
class | 接口上注解 |
使用Spring管理Mybatis
这里的管理指的是
- 数据源
- sqlSession
- Mapper
在Spring-context.xml中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!-- 数据源 -->
<!-- 或者 com.mchange.v2.c3p0.ComboPooledDataSource 什么的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="toor" />
</bean>
<!-- sqlSession(Factory) -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Mapper -->
<!-- 手动 -->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.test.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- 自动 -->
<bean id="MapperScannerConfig" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.mapper"/>
</bean>
这样搞完后,只需要
- 写好DAO层调数据库的接口,并写好操作数据库的sql语句(xml/注解),自动注入Mapper的配置
- 在Service层中配置SqlSession类型的变量,即可注入sqlSession,调用DAO层的接口
- 在Controller层调用Service层的方法,并使得SpringMVC可以传递输入输出数据
- Tomcat等Servlet容器在启动之后将特定的数据传递到SpringMVC
- 浏览器将其转换成从Tomcat或其他http服务器获得数据,展示给用户
分页
使用阿里巴巴的pagehelper即可,有中文文档
一对多,多对一,多对多
在实体类中设置某其他实体类变量即可实现多对多的功能
Mybatis Generator
maven中添加mybatis-generator-maven-plugin(org.mybatis.generator)插件
可自动生成mybatis所需的各种文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42"1.0" encoding="UTF-8" xml version=
<generatorConfiguration>
<classPathEntry location="(jdbc驱动路径)"/>
<context id="context" targetRuntime="MyBatis3Simple">
<commentGenerator>
<property name="suppressAllComments" value="false"/>
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- jdbc连接信息 -->
<jdbcConnection userId="" password="" driverClass="" connectionURL=""/>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- Model构造器 -->
<javaModelGenerator targetPackage="" targetProject=".">
<property name="enableSubPackages" value="false"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- mapper.xml构造器 -->
<sqlMapGenerator targetPackage="" targetProject=".">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- mapper接口构造器 -->
<javaClientGenerator targetPackage="" type="XMLMAPPER" targetProject=".">
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 指定表(table) -->
<table schema="" tableName="" enableCountByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" enableUpdateByExample="false"/>
</context>
</generatorConfiguration>
参考链接
详解 Spring 3.0 基于 Annotation 的依赖注入实现