[scode type="share"]假期接了个Android App的项目，一开始想学着做原生的App，后来因为考虑到学习成本，用MUI框架开发。看了一点点原生App开发，记录下。[/scode]Begin项目目录结构HelloWorldUI设计使用图形界面进行设计，注意多分辨率设备的适配。生命周期package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity { String TAG = "myTAG"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate: "); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart: "); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume: "); } @Override protected void onPause() { super.onPause(); Log.d(TAG, "onPause: "); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop: "); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart: "); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: "); } }屏幕翻转的时候也会重新启动生命周期。控件入门通过ID修改Textpublic class MainActivity extends AppCompatActivity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView3); // R.drawable.xx; // R.raw.xx; // R.layout.xx; } @Override protected void onPause() { super.onPause(); textView.setText("Pause"); } }Button的监听器package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { TextView textView; Button button1, button2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.textView4); button1 = findViewById(R.id.button3); button2 = findViewById(R.id.button4); // 监听器 button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { textView.setText("左边"); } }); button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { textView.setText("右边"); } }); } }国际化有时还要对layout设计多份。屏幕旋转及状态保存屏幕旋转以后activity会摧毁并创建。可以重新设计屏幕旋转的layout。public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) { String s = savedInstanceState.getString("KEY"); textView.setText(s); } } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putString("KEY", textView.getText().toString()); } }
IoC控制反转反转的是获取对象的方式。传统 new对象，SpringIoC容器只需要 getBean("obj")从容器中获取(applicationContext.xml)为了方便理解，IoC改名为DI依赖注入两种理解：控制反转：将创建对象、属性值的方式 进行了反转，从 new、setObj()反转为了 从springIoC容器 getBean()依赖注入：将属性值注入给了属性，将属性注入给了 bean，将 bean注入给了IoC容器案例创建Student实例类package com.moluuser.entity; public class Student { private int stuNo; private String stuName; private int stuAge; public int getStuNo() { return stuNo; } public void setStuNo(int stuNo) { this.stuNo = stuNo; } public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } public int getStuAge() { return stuAge; } public void setStuAge(int stuAge) { this.stuAge = stuAge; } @Override public String toString() { return "Student{" + "stuNo=" + stuNo + ", stuName='" + stuName + '\'' + ", stuAge=" + stuAge + '}'; } }applicationContext.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.moluuser.entity.Student"> <property name="stuName" value="zs"></property> <property name="stuAge" value="18"></property> <property name="stuNo" value="5"></property> </bean> </beans>测试类package com.moluuser.text; import com.moluuser.entity.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.applet.AppletContext; public class Text { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); } }Add创建IoCpublic class Client { /** * 获取spring的Ioc核心容器，并根据id获取对象 * * ApplicationContext的三个常用实现类： * ClassPathXmlApplicationContext：它可以加载类路径下的配置文件，要求配置文件必须在类路径下。不在的话，加载不了。(更常用) * FileSystemXmlApplicationContext：它可以加载磁盘任意路径下的配置文件(必须有访问权限） * * AnnotationConfigApplicationContext：它是用于读取注解创建容器的，是明天的内容。 * * 核心容器的两个接口引发出的问题： * ApplicationContext: 单例对象适用 采用此接口 * 它在构建核心容器时，创建对象采取的策略是采用立即加载的方式。也就是说，只要一读取完配置文件马上就创建配置文件中配置的对象。 * * BeanFactory: 多例对象使用 * 它在构建核心容器时，创建对象采取的策略是采用延迟加载的方式。也就是说，什么时候根据id获取对象了，什么时候才真正的创建对象。 * @param args */ public static void main(String[] args) { //1.获取核心容器对象 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); // ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\zhy\\Desktop\\bean.xml"); //2.根据id获取Bean对象 IAccountService as = (IAccountService)ac.getBean("accountService"); IAccountDao adao = ac.getBean("accountDao",IAccountDao.class); System.out.println(as); System.out.println(adao); as.saveAccount(); } }bean作用范围bean的作用范围调整 bean标签的scope属性： 作用：用于指定bean的作用范围 取值： 常用的就是单例的和多例的 singleton：单例的（默认值） prototype：多例的 request：作用于web应用的请求范围 session：作用于web应用的会话范围 global-session：作用于集群环境的会话范围（全局会话范围），当不是集群环境时，它就是sessionbean生命周期bean对象的生命周期 单例对象 出生：当容器创建时对象出生 活着：只要容器还在，对象一直活着 死亡：容器销毁，对象消亡 总结：单例对象的生命周期和容器相同 多例对象 出生：当我们使用对象时spring框架为我们创建 活着：对象只要是在使用过程中就一直活着。 死亡：当对象长时间不用，且没有别的对象引用时，由Java的垃圾回收器回收依赖注入IoC容器赋值如果是简单类型（8个基本+String），value；如果是对象类型，ref="需要引用的id值"，因此实现了对象与对象之间的依赖关系。在IoC中定义bean的前提：该bean的类必须提供了无参构造 <bean id="teacher" class="com.moluuser.entity.Teacher"> <property name="name" value="ls"></property> <property name="student" ref="student"></property> </bean>set注入通过 setObj()赋值默认使用的是set方法()，依赖注入底层是通过反射实现的。使用 </property>构造器注入通过构造方法赋值<constructor-arg value="ls" type="String" index="0" name="name"></constructor-arg>注意：如果 <constructor-arg>的顺序与构造方法参数的顺序不一致，则需要通过type或index或name指定。p命名空间注入在beans标签中加入xmlns:p="http://www.springframework.org/schema/p"使用以下语句赋值 <bean id="teacher" class="com.moluuser.entity.Teacher" p:name="zs" p:student-ref="student"></bean>集合赋值注入各种集合数据类型: List Set map properties...set、list、数组各自都有自己的标签<set> <array>，但是也可以混着用注入案例<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--构造函数注入： 使用的标签:constructor-arg 标签出现的位置：bean标签的内部 标签中的属性 type：用于指定要注入的数据的数据类型，该数据类型也是构造函数中某个或某些参数的类型 index：用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始 name：用于指定给构造函数中指定名称的参数赋值 常用的 =============以上三个用于指定给构造函数中哪个参数赋值=============================== value：用于提供基本类型和String类型的数据 ref：用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象 优势： 在获取bean对象时，注入数据是必须的操作，否则对象无法创建成功。 弊端： 改变了bean对象的实例化方式，使我们在创建对象时，如果用不到这些数据，也必须提供。 --> <bean id="accountService" class="com.moluuser.service.impl.AccountServiceImpl"> <constructor-arg name="name" value="泰斯特"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="now"></constructor-arg> </bean> <bean id="now" class="java.util.Date"></bean> <bean id="accountService2" class="com.moluuser.service.impl.AccountServiceImpl2"> <property name="name" value="TEST" ></property> <property name="age" value="21"></property> <property name="birthday" ref="now"></property> </bean> <bean id="accountService3" class="com.moluuser.service.impl.AccountServiceImpl3"> <property name="myStrs"> <set> <value>AAA</value> <value>BBB</value> <value>CCC</value> </set> </property> <property name="myList"> <array> <value>AAA</value> <value>BBB</value> <value>CCC</value> </array> </property> <property name="mySet"> <value>AAA</value> <value>BBB</value> <value>CCC</value> </property> <property name="myMap"> <props> <prop key="testC">ccc</prop> <prop key="testD">ddd</prop> </props> </property> <property name="myProps"> <entry key="testA" value="aaa"></entry> <entry key="testB"> <value>BBB</value> </entry> </property> </bean> </beans>两种注入方式区别and给对象类型赋值 null <property name="name" > <null/> -->注意 没有<value> </property>赋空值 "" <property name="name" > <value></value> </property>自动装配用 ref才可以，基本类型不行<bean ... class="com.moluuser.entity.Course" autowire="byName|byType|constructor|no" > (byName本质是byId)byName：自动寻找：其他bean的id值=该Course类的属性名byType：其他bean的类型(class) 是否与 该Course类的ref属性类型一致 （注意，此种方式必须满足：当前IoC容器中,只能有一个Bean满足条件）constructor：其他bean的类型(class) 是否与该Course类的构造方法参数的类型一致；此种方式的本质就是byType可以在头文件中一次性将该IoC容器的所有bean 统一设置成自动装配：<beans xmlns="http://www.springframework.org/schema/beans" ... default-autowire="byName">自动装配虽然可以减少代码量，但是会降低程序的可读性，使用时需要谨慎。使用注解定义bean通过注解的形式将bean以及相应的属性值放入IoC容器扫描器<context:component-scan base-package="com.moluuser.dao">`</context:component-scan>Spring在启动的时候，会根据base-package在 该包中扫描所有类，查找这些类是否有注解 @Component("studentDao")`,如果有，则将该类加入Spring IoC容器。等价写法 创建对象的注解@Component范围大，可以细化：dao层注解：@Repositoryservice层注解：@Service控制器层注解：@Controller用于创建对象的 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的 Component: 作用：用于把当前类对象存入spring容器中 属性： value：用于指定bean的id。当我们不写时，它的默认值是当前类名，且首字母改小写。 Controller：一般用在表现层 Service：一般用在业务层 Repository：一般用在持久层 以上三个注解他们的作用和属性与Component是一模一样。 他们三个是spring框架为我们提供明确的三层使用的注解，使我们的三层对象更加清晰注入数据的注解用于注入数据的 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的 Autowired: 作用：自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配，就可以注入成功 如果IoC容器中没有任何bean的类型和要注入的变量类型匹配，则报错。 如果Ioc容器中有多个类型匹配时： 出现位置： 可以是变量上，也可以是方法上 细节： 在使用注解注入时，set方法就不是必须的了。 Qualifier: 作用：在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以（稍后我们讲） 属性： value：用于指定注入bean的id。 Resource 作用：直接按照bean的id注入。它可以独立使用 属性： name：用于指定bean的id。 以上三个注入都只能注入其他bean类型的数据，而基本类型和String类型无法使用上述注解实现。 另外，集合类型的注入只能通过XML来实现。 Value 作用：用于注入基本类型和String类型的数据 属性： value：用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式） SpEL的写法：${表达式}改变作用范围的注解用于改变作用范围的 他们的作用就和在bean标签中使用scope属性实现的功能是一样的 Scope 作用：用于指定bean的作用范围 属性： value：指定范围的取值。常用取值：singleton prototype生命周期相关注解和生命周期相关 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的 PreDestroy 作用：用于指定销毁方法 PostConstruct 作用：用于指定初始化方法新注解@Configuration 作用： 用于指定当前类是一个spring配置类，当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)。 属性： value:用于指定配置类的字节码 @ComponentScan 作用： 用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的： <context:component-scan base-package="com.itheima"/>是一样的。 属性： basePackages：用于指定要扫描的包。和该注解中的value属性作用一样。 @Bean 作用： 该注解只能写在方法上，表明使用此方法创建一个对象，并且放入spring容器。 属性： name：给当前@Bean注解方法创建的对象指定一个名称(即bean的id）。 @PropertySource 作用： 用于加载.properties文件中的配置。例如我们配置数据源时，可以把连接数据库的信息写到properties配置文件中，就可以使用此注解指定properties配置文件的位置。 属性： value[]：用于指定properties文件位置。如果是在类路径下，需要写上classpath: @Import 作用： 用于导入其他配置类，在引入其他配置类时，可以不用再写@Configuration注解。当然，写上也没问题。 属性： value[]：用于指定其他配置类的字节码。AOP面向切面作用： 在程序运行期间，不修改源码对已有方法进行增强。优势： 减少重复代码 提高开发效率 维护方便相关术语Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。 Pointcut(切入点): 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 Advice(通知/增强): 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。 通知的类型：前置通知,后置通知,异常通知,最终通知,环绕通知。 Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field。 Target(目标对象): 代理的目标对象。 Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring采用动态代理织入，而AspectJ采用编译期织入和类装载期织入。 Proxy（代理）: 一个类被AOP织入增强后，就产生一个结果代理类。 Aspect(切面): 是切入点和通知（引介）的结合。接口实现AOP配置 appclicationContext.xml <aop:config> <aop:pointcut id="" expression=""/> <aop:advisor advice-ref=""></aop:advisor> </aop:config>环绕通知：在目标方法的前后、异常发生时、最终等各个地方都可以进行的通知，最强大的一个通知。可以获取目标方法的 全部控制权（目标方法是否执行、执行之前、执行之后、参数、返回值等）在使用环绕通知时，目标方法的一切信息都可以通过invocation参数获取到环绕通底层是通过拦截器实现的。注解实现AOP@Aspect //声明该类是一个通知 public class LogBeforeAnnotation { }注意：通过注解形式将对象增加到IoC容器时，需要设置扫描器 <context:component-scan base-package="com.moluuser.aop"></context:component-scan>扫描器会将指定的包中的 @Componet @Service @Respository @Controller修饰的类产生的对象增加到IoC容器中@Aspect不需要加入扫描器，只需要开启即可：<aop:aspectj-autoproxy></aop:aspectj-autoproxy>通过注解形式实现的AOP，如果想获取目标对象的一些参数，则需要使用一个对象：JointPoint注解形式的返回值声明返回值的参数名 @AfterReturning(pointcut = "execution(public * addStudent(..))", returning = "returningValue") public void myAfter(JoinPoint jp, Object returningValue) { //returningValue是返回值，但需要告诉spring System.out.println("返回值：" + returningValue);配置实现 类->通知基于Schema配置接口方式通知：public class LogAfter implements AfterReturningAdvice()Schema方式通知：编写一个普通类 public class LogAfter {}将该类通过配置，转为一个"通知"Spring开发Web当服务启动时（tomcat），通过监听器将SpringIoC容器初始化一次（该监听器 spring-web.jar已经提供）在 web.xml中配置 <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>拆分Spring配置文件Web项目：根据什么拆分？三层结构UI(html/css/jsp,Servlet):applicationController.xmlService:applicationService.xmlDao:applicationDao.xml公共数据库:applicationDB.xml功能结构学生相关配置 applicationContextStudent.xml <bean id="" class="X...Student">班级相关配置 applicationContextClass.xml合并：如何将多个配置文件加载第一种<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml, classpath:applicationContext-Dao.xml, classpath:applicationContext-Service.xml, classpath:applicationContext-Controller.xml </param-value> </context-param>第二种<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:applicationContext.xml, classpath:applicationContext-*.xml </param-value> </context-param>第三种只在 web.xml中加载主配置文件 <param-value> classpath:applicationContext.xml </param-value>然后在主配置问加中，加载其他配置文件 <import resource="applicationContext-*.xml"/>Servlet容器和SpringIoC的桥梁
Begin前身iBatis，由Apache->GoogleMyBatis可以简化JDBC操作，实现数据的持久化。ORM对象-关系映射（Object/Relation Mapping，简称ORM），是随着面向对象的软件开发方法发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流开发方法，关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式，业务实体在内存中表现为对象，在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系，而在数据库中，关系数据无法直接表达多对多关联和继承关系。因此，对象-关系映射(ORM)系统一般以中间件的形式存在，主要实现程序对象到关系数据库数据的映射。MyBatis是ORM的一个实现，持久层框架。相关还有Hibernate, Spring Data JPA等ORM使得的开发人员像操作对象一样操作数据库表。MyBatis是一个优秀的基于java的持久层框架，它内部封装了jdbc，使开发者只需要关注SQL语句本身，而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。 MyBatis通过xml或注解的方式将要执行的各种statement配置起来，并通过java对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句，最后由MyBatis框架执行SQL并将结果映射为java对象并返回。 采用ORM思想解决了实体和数据库映射的问题，对jdbc进行了封装，屏蔽了jdbc api底层访问细节，使我们不用与jdbc api打交道，就可以完成对数据库的持久化操作。MyBatis入门数据库准备DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL auto_increment, `username` varchar(32) NOT NULL COMMENT '用户名称', `birthday` datetime default NULL COMMENT '生日', `sex` char(1) default NULL COMMENT '性别', `address` varchar(256) default NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');创建maven工程并导入坐标 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> </dependencies>创建实体类和dao的接口实体类：package com.moluuser; import java.io.Serializable; import java.util.Date; public class User implements Serializable { // 和数据库命名保持一致 private Integer id; private String username; private Date birthday; private String sex; private String address; @Override public String toString() { return "user{" + "id=" + id + ", username='" + username + '\'' + ", birthday=" + birthday + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }package com.moluuser.dao; import com.moluuser.User; import java.util.List; // 用户的持久层操作 public interface IUserDao { List<User> findAll(); }创建MyBatis的主配置文件在 resource下创建 SqlMapConfig.xml<?xml version="1.0" encoding="UTF-8"?> <configuration> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://moluuser.cn:3306/user"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com.moluuser/dao/IUserDao.xml"></mapper> </mappers> </configuration>创建映射配置文件在 resources\com.moluuser\dao下创建 IUserDao.xmlMyBatis的映射配置文件位置必须和dao接口的包结构相同映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名映射配置文件的操作配置（select），id属性的取值必须是dao接口的方法名<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="com.moluuser.dao.IUserDao"> SELECT * from user </mapper>创建测试类package com.moluuser.test; import com.moluuser.User; import com.moluuser.dao.IUserDao; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyBatisTest { public static void main(String[] args) throws IOException { // 读取配置文件 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); // 使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); // 使用SqlSession创建Dao接口的代理对象 IUserDao userDao = session.getMapper(IUserDao.class); // 使用代理对象执行方法 List<User> users = userDao.findAll(); for (User user: users) { System.out.println(user); } // 释放资源 session.close(); in.close(); } }运行目录结构得到如下结果我们在实际开发中，都是越简便越好，所以都是采用不写dao实现类的方式。不管使用XML还是注解配置。但是MyBatis它是支持写dao实现类的。设计模式分析读取配置文件ServletContext对象的 getRealPath()获取路径创建SqlSessionFactory工厂建造者模式（Builder Pattern）使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式，它提供了一种创建对象的最佳方式。使用工厂生产SqlSession不需要使用 new解耦，降低类之间的依赖关系使用SqlSession创建Dao接口的代理对象代理模式：在代理模式（Proxy Pattern）中，一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。在代理模式中，我们创建具有现有对象的对象，以便向外界提供功能接口。执行dao中的方法释放资源CRUD增加(Create)、读取(Retrieve)、更新(Update)和删除(Delete)测试类MyBatisTest.classpackage com.moluuser.test; import com.moluuser.User; import com.moluuser.dao.IUserDao; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class MyBatisTest { private InputStream in = null; private SqlSession session = null; private IUserDao userDao; @Before public void init() throws IOException { in = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); session = factory.openSession(); userDao = session.getMapper(IUserDao.class); } @After public void destroy() throws IOException { session.close(); in.close(); } @Test public void testFindAll() throws IOException { List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } // 增加 @Test public void testSave() { User user = new User(); user.setAddress("Mars"); user.setUsername("Alpha"); user.setSex("男"); user.setBirthday(new Date()); userDao.saveUser(user); // 提交事务 session.commit(); } // 更新 @Test public void testUpdate() { User user = new User(); user.setAddress("Mars"); user.setUsername("Alpha"); user.setSex("男"); user.setId(48); user.setBirthday(new Date()); userDao.updateUser(user); session.commit(); } @Test public void testDelete() { userDao.deleteUser(46); session.commit(); } @Test public void testFindById() { User user = userDao.findById(51); System.out.println(user.toString()); } @Test public void findByName() { List<User> users = userDao.findByName("%l%"); for (User user : users) { System.out.println(user.toString()); } } } DAO接口IUserDaopackage com.moluuser.dao; import com.moluuser.User; import java.util.List; // 用户的持久层操作 public interface IUserDao { List<User> findAll(); void saveUser(User user); void updateUser(User user); void deleteUser(Integer uid); User findById(Integer id); List<User> findByName(String s); }IUserDao.xml<?xml version="1.0" encoding="UTF-8"?> <mapper namespace="com.moluuser.dao.IUserDao"> SELECT * from user <insert id="saveUser" parameterType="com.moluuser.User"> insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday}) </insert> <update id="updateUser" parameterType="com.moluuser.User"> update user set username=#{username}, address=#{address}, sex=#{sex}, birthday=#{birthday} where id = #{id} </update> <delete id="deleteUser" parameterType="Integer"> -- 有一个参数时，{uid}中的参数随便写，只是占位符 delete from user where id=#{uid} </delete> select * from user where id = #{id} select * from user where username like #{name} -- select * from user where username like '%${value}%' -- 这个是使用statement实现的，不推荐 </mapper>新增用户后，同时还要返回当前新增用户的id值，因为id是由数据库的自动增长来实现的，所以就相当于我们要在新增后将自动增长auto_increment的值返回。 <insert id="saveUser" parameterType="USER"> <selectKey keyColumn="id" keyProperty="id" resultType="int"> select last_insert_id(); </selectKey> insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert>执行SQL后，user的id属性就会被赋值MyBatis的参数深入parameterType配置参数基本类型和String我们可以直接写类型名称，也可以使用包名.类名的方式，例如：java.lang.String。 实体类类型，目前我们只能使用全限定类名。 究其原因，是mybaits在加载时已经把常用的数据类型注册了别名，从而我们在使用时可以不写包名，而我们的是实体类并没有注册别名，所以必须写全限定类名。在今天课程的最后一个章节中将讲解如何注册实体类的别名。在MyBatis的官方文档的说明OGNL表达式它是一门表达式语言，除了用来设置和获取Java对象的属性之外，另外提供诸如集合的投影和过滤以及lambda表达式等ObjectGraphicNavigationLanguage对象图导航语言使用实体类的包装对象作为查询条件查询的实体类package com.moluuser; public class QueryVo { public User getUser() { return user; } public void setUser(User user) { this.user = user; } private User user; }IUserDao.xml select * from user where username like #{user.username} 测试类// 使用QueryVo作为查询条件 @Test public void testFindByVo() { QueryVo vo = new QueryVo(); User user = new User(); user.setUsername("%l%"); vo.setUser(user); List<User> users = userDao.findUserByVo(vo); for (User us: users) { System.out.println(us); } }解决实体类属性和数据库列名不对应的两种方式SQL语句起别名(效率高) 配置 IUserDao.xmlMyBatis传统DAO层开发使用MyBatis开发Dao，通常有两个方法，即原始Dao开发方式和Mapper接口代理开发方式。而现在主流的开发方式是接口代理开发方式，这种方式总体上更加简便。仅为了解内容持久层Dao接口public interface IUserDao { /** * 查询所有用户 * @return */ List<User> findAll(); /** * 根据id查询 * @param userId * @return */ User findById(Integer userId); /** * 保存用户 * @param user * @return 影响数据库记录的行数 */ int saveUser(User user); /** * 更新用户 * @param user* @return 影响数据库记录的行数 */ int updateUser(User user); /** * 根据id删除用户 * @param userId * @return */ int deleteUser(Integer userId); /** * 查询总记录条数 * @return */ int findTotal(); }持久层Dao实现类public class UserDaoImpl implements IUserDao { private SqlSessionFactory factory; public UserDaoImpl(SqlSessionFactory factory) { this.factory = factory; } @Override public List<User> findAll() { SqlSession session = factory.openSession(); List<User> users = session.selectList("com.itheima.dao.IUserDao.findAll"); session.close(); return users; } @Overridepublic User findById(Integer userId) { SqlSession session = factory.openSession(); User user = session.selectOne("com.itheima.dao.IUserDao.findById", userId); session.close(); return user; } @Override public int saveUser(User user) { SqlSession session = factory.openSession(); int res = session.insert("com.itheima.dao.IUserDao.saveUser", user); session.commit(); session.close(); return res; } @Override public int updateUser(User user) { SqlSession session = factory.openSession(); int res = session.update("com.itheima.dao.IUserDao.updateUser", user); session.commit(); session.close(); return res; } @Override public int deleteUser(Integer userId) { SqlSession session = factory.openSession(); int res = session.delete("com.itheima.dao.IUserDao.deleteUser", userId); session.commit(); session.close(); return res; } @Override public int findTotal() { SqlSession session = factory.openSession(); int res = session.selectOne("com.itheima.dao.IUserDao.findTotal"); session.close(); return res; } }持久层映射配置测试类SqlMapConfig.xml配置文件SqlMapConfig.xml中配置的内容和顺序properties（属性）在使用properties标签配置时，我们可以采用两种方式指定属性配置。第一种<properties> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://moluuser.cn:3306/user?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="password"/> </properties>第二种在classpath下定义db.properties文件jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/eesy jdbc.username=root jdbc.password=1234properties标签配置此时我们的dataSource标签就变成了引用上面的配置<dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource>typeAliases（类型别名）自定义别名<typeAliases> <typeAlias alias="user" type="com.itheima.domain.User"/> <package name="com.itheima.domain"/> <package name="其它包"/> </typeAliases>mappers（映射器）<mapper resource=" " />使用相对于类路径的资源 如：<mapper resource="com/itheima/dao/IUserDao.xml" /><mapper class=" " />使用mapper接口类路径如：<mapper class="com.itheima.dao.UserDao"/>注意：此种方法要求mapper接口名称和mapper映射文件名称相同，且放在同一个目录中。<package name=""/>注册指定包下的所有mapper接口如：<package name="cn.itcast.mybatis.mapper"/>注意：此种方法要求mapper接口名称和mapper映射文件名称相同，且放在同一个目录中。连接池在MyBatis的SqlMapConfig.xml配置文件中，通过 <dataSource type=”pooled”>来实现MyBatis中连接池的配置。在MyBatis中我们将它的数据源dataSource分为以下几类：type描述POOLED采用传统的 javax.sql.DataSource规范中的连接池，MyBatis中有针对规范的实现UNPOOLED采用传统的获取连接的方式，虽然也实现 Javax.sql.DataSource接口，但是并没有使用池的思想。JNDI采用服务器提供的JNDI技术实现，来获取 DataSource对象，不同的服务器所能拿到 DataSource是不一样。注意：如果不是web或者maven的war工程，是不能使用的。我们使用的是tomcat服务器，采用连接池就是dbcp连接池。 <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://moluuser.cn:3306/user?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource>MyBatis中的事务事务的定义事务（Transaction）是并发控制的基本单位。所谓的事务，它是一个操作序列，这些操作要么都执行，要么都不执行，它是一个不可分割的工作单位。事务的四大特性ACIDAtomic（原子性）：事务中包含的操作被看做一个逻辑单元，这个逻辑单元中的操作要么全部成功，要么全部失败。Consistency（一致性）：只有合法的数据可以被写入数据库，否则事务应该将其回滚到最初状态。Isolation（隔离性）：事务允许多个用户对同一个数据进行并发访问，而不破坏数据的正确性和完整性。同时，并行事务的修改必须与其他并行事务的修改相互独立。Durability（持久性）：事务结束后，事务处理的结果必须能够得到固化。不考虑隔离性会产生的3个问题脏读脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。当一个事务正在多次修改某个数据，而在这个事务中这多次的修改都还未提交，这时一个并发的事务来访问该数据，就会造成两个事务得到的数据不一致。例如：用户A向用户B转账100元，对应SQL命令如下 update account set money=money+100 where name=’B’; (此时A通知B) update account set money=money - 100 where name=’A’;当只执行第一条SQL时，A通知B查看账户，B发现确实钱已到账（此时即发生了脏读），而之后无论第二条SQL是否执行，只要该事务不提交，则所有操作都将回滚，那么当B以后再次查看账户时就会发现钱其实并没有转。不可重复读不可重复读是指在对于数据库中的某个数据，一个事务范围内多次查询却返回了不同的数据值，这是由于在查询间隔，被另一个事务修改并提交了。例如事务T1在读取某一数据，而事务T2立马修改了这个数据并且提交事务给数据库，事务T1再次读取该数据就得到了不同的结果，发送了不可重复读。不可重复读和脏读的区别是，脏读是某一事务读取了另一个事务未提交的脏数据，而不可重复读则是读取了前一事务提交的数据。虚读(幻读)幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作，这时事务T2又对这个表中插入了一行数据项，而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据，会发现还有一行没有修改，其实这行是从事务T2中添加的，就好像产生幻觉一样，这就是发生了幻读。幻读和不可重复读都是读取了另一条已经提交的事务（这点就脏读不同），所不同的是不可重复读查询的都是同一个数据项，而幻读针对的是一批数据整体（比如数据的个数）。解决办法：四种隔离级别Serializable (串行化)：可避免脏读、不可重复读、幻读的发生。Repeatable read (可重复读)：可避免脏读、不可重复读的发生。Read committed (读已提交)：可避免脏读的发生。Read uncommitted (读未提交)：最低级别，任何情况都无法保证。MyBatis自动提交事务的设置通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚MyBatis的动态SQL语句if标签我们根据实体类的不同取值，使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询，如果username不为空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。持久层Dao接口/** * 根据用户信息，查询用户列表 * * @param user * * @return * */ List<User> findByUser(User user);持久层Dao映射配置测试 @Test public void testFindByUser() { User u = new User(); u.setUsername("%name%"); u.setAddress("%address%"); // 执行操作 List<User> users = userDao.findByUser(u); for (User user : users) { System.out.println(user); } }where标签为了简化上面where 1=1的条件拼装，我们可以采用 <where>标签来简化开发。持久层Dao映射配置forecho标签需求传入多个id查询用户信息，用下边两个SQL实现：SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16) SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16) 这样我们在进行范围查询时，就要将一个集合中的值，作为参数动态添加进来。在QueryVo中加入一个List集合用于封装参数public class QueryVo implements Serializable { private List<Integer> ids; public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }持久层Dao接口 List<User> findInIds(QueryVo vo);持久层Dao映射配置测试方法MyBatis中简化编写的SQL片段SQL中可将重复的SQL提取出来，使用时用include引用即可，最终达到SQL重用的目的。定义代码片段 <sql id="defaultSql"> select * from user </sql>引用代码片段 <include refid="defaultSql"></include> <include refid="defaultSql"></include> where id = #{uid} MyBatis多表查询一对一查询方法一使用子类为了能够封装上面SQL语句的查询结果，定义 AccountCustomer类中要包含账户信息同时还要包含用户信息，所以我们要在定义AccountUser类时可以继承User类。方法二定义实体类关系在Account类中加入User类的对象作为Account类的一个属性。重新定义 AccountDao.xml文件 一对多查询SQL User类加入List<Account>用户持久层Dao映射文件配置 多对多延迟加载策略延迟加载：就是在需要用到数据时才进行加载，不需要用到数据时就不加载数据。延迟加载也称按需加载，懒加载.好处：先从单表查询，需要时再从关联表去关联查询，大大提高数据库性能，因为查询单表要比关联查询多张表速度要快。坏处：因为只有当需要用到数据时，才会进行数据库查询，这样在大批量数据查询时，因为查询工作也要消耗时间，所以可能造成用户等待时间变长，造成用户体验下降。使用assocation实现延迟加载使用collection实现延迟加载MyBatis缓存什么是缓存：存在于内存中的临时数据。为什么使用缓存：减少和数据库的交互次数，提高执行效率。什么样的数据能使用缓存，什么样的数据不能使用适用于缓存： 经常查询并且不经常改变的。数据的正确与否对最终结果影响不大的。不适用于缓存： 经常改变的数据数据的正确与否对最终结果影响很大的。 例如：商品的库存，银行的汇率，股市的牌价。MyBatis中的一级缓存和二级缓存一级缓存：它指的是MyBatis中SqlSession对象的缓存。 当我们执行查询之后，查询的结果会同时存入到SqlSession为我们提供一块区域中。 该区域的结构是一个Map。当我们再次查询同样的数据，MyBatis会先去sqlsession中 查询是否有，有的话直接拿出来用。 当SqlSession对象消失时，MyBatis的一级缓存也就消失了。 当调用SqlSession的修改，添加，删除，commit()，close()等方法时，就会清空一级缓存。二级缓存:它指的是MyBatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。 二级缓存的使用步骤： 第一步：让MyBatis框架支持二级缓存（在SqlMapConfig.xml中配置） 第二步：让当前的映射文件支持二级缓存（在IUserDao.xml中配置） 第三步：让当前的操作支持二级缓存（在select标签中配置）二级缓存的开启与关闭第一步：在SqlMapConfig.xml文件开启二级缓存<settings> <setting name="cacheEnabled" value="true"/> </settings>因为cacheEnabled的取值默认就为true，所以这一步可以省略不配置。为true代表开启二级缓存；为false代表不开启二级缓存。第二步：配置相关的Mapper映射文件<cache>标签表示当前这个mapper映射将使用二级缓存，区分的标准就看mapper的namespace值。 <?xml version="1.0" encoding="UTF-8"?> <mapper namespace="com.itheima.dao.IUserDao"> <cache></cache> </mapper>第三步：配置statement上面的useCache属性 select * from user where id = #{uid} 将UserDao.xml映射文件中的 标签中设置useCache="true"代表当前这个statement要使用二级缓存，如果不使用二级缓存可以设置为false。注意：针对每次查询都需要最新的数据sql，要设置成useCache=false，禁用二级缓存。当我们在使用二级缓存时，所缓存的类一定要实现java.io.Serializable接口，这种就可以使用序列化方式来保存对象。注解开发常用注解@Insert:实现新增 @Update:实现更新 @Delete:实现删除 @Select:实现查询 @Result:实现结果集封装 @Results:可以与@Result一起使用，封装多个结果集 @ResultMap:实现引用 @Results定义的封装 @One:实现一对一结果集封装 @Many:实现一对多结果集封装 @SelectProvider:实现动态SQL映射 @CacheNamespace:实现注解二级缓存的使用单表CRUD操作（代理Dao方式）不需要 IUserDao.xml直接写 IUserDao.classpackage com.moluuser.dao; import com.moluuser.User; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import java.util.List; public interface IUserDao { @Select("select * from user") List<User> findAll(); @Insert("insert into user(username, address, sex, birthday) values(#{username}, #{address}, #{sex}, #{birthday})") void saveUser(User user); }SqlMapConfig.xml<?xml version="1.0" encoding="UTF-8"?> <configuration> <properties resource="jdbcConfig.properties"></properties> <typeAliases> <package name="com.moluuser"></package> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <package name="com.moluuser.dao"/> </mappers> </configuration>jdbcConfig.propertiesjdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://moluuser.cn:3306/user?useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=password多表查询操作实现复杂关系映射之前我们可以在映射文件中通过配置 <resultMap>来实现，在使用注解开发时我们需要借助@Results注解，@Result注解，@One注解，@Many注解。缓存的配置在SqlMapConfig中开启二级缓存支持 <settings> <setting name="cacheEnabled" value="true"/> </settings>在持久层接口中使用注解配置二级缓存@CacheNamespace(blocking=true)//mybatis基于注解方式实现配置二级缓存 public interface IUserDao {}参考链接：https://www.cnblogs.com/fjdingsd/p/5273008.html
简介简化Spring应用开发的一个框架整个Spring技术栈的一个大整合J2EE开发的一站式解决方案微服务一个应用应该是一组小型服务；可以通过HTTP的方式进行互通；单体应用：ALL IN ONE微服务：每一个功能元素最终都是一个可独立替换和独立升级的软件单元HelloWorld导入依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>主程序package com.moluuser; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.Spring BootApplication; @SpringBootApplication public class HelloWorld { public static void main(String[] args) { SpringApplication.run(HelloWorld.class, args); } }Controllerpackage com.moluuser.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { @ResponseBody @RequestMapping("/hello") public String hello() { return "Hello World"; } }Run部署加入插件 <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>maven packagejava -jar packageName即可完成部署，不需要Tomcat环境HelloWorld解析POM文件导入依赖spring-boot-starter-parent是一个特殊的 starter，它用来提供相关的 Maven 默认依赖。 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent>其父工程 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>1.5.9.RELEASE</version> <relativePath>../../spring-boot-dependencies</relativePath> </parent>spring-boot-dependencies定义了Spring Boot所有的依赖的版本Spring Boot的版本仲裁中心: 以后我们导入依赖默认是不需要写版本(没有在dependencies里面管理的依赖自然需要声明版本号)导入启动器 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>spring-boot-starter-web: spring-boot场景启动器,帮我们导入了web模块正常运行所依赖的组件Spring Boot将所有的功能场景都抽取出来，做成一个个的starters（启动器），只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器。主程序类@SpringBootApplication(Spring Boot应用)标注在某个类上说明这个类是Spring Boot的主配置类，Spring Boot就会运行这个类的main方法来启动Spring Boot应用组合注解：@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} )自动配置以后填坑...IDEA创建Spring Initializr主程序框架自动生成，只需要写业务逻辑resources文件夹中目录结构static：保存所有的静态资源； js css imagestemplates：保存所有的模板页面，（Spring Boot默认jar包使用嵌入式的Tomcat，默认不支持JSP页面），可以使用模板引擎（freemarker、thymeleaf）application.properties：Spring Boot应用的配置文件；可以修改一些默认设置
JNDIJava命名与目录接口配置tomcat/conf/context.xml配置： <Environment name="jndiName" value="jndiValue" type="java.lang.String" />JSP中使用 <% Context ctx = new InitialContext() ; String testJndi = (String)ctx.lookup("java:comp/env/jndiName"); out.print(testJndi); %>连接池数据库连接池（Connection pooling）是程序启动时建立足够的数据库连接，并将这些连接组成一个连接池，由程序动态地对池中的连接进行申请，使用，释放。创建数据库连接是一个很耗时的操作，也容易对数据库造成安全隐患。所以，在程序初始化的时候，集中创建多个数据库连接，并把他们集中管理，供程序使用，可以保证较快的数据库读写速度，还更加安全可靠。常见连接池：Tomcat-dbcp、dbcp、c3p0、druid可以用数据源(javax.sql.DataSource)管理连接池Tomcat-dbcp配置数据源（context.xml）类似jndi,在context.xml中配置数据库 <Resource name="student" auth="Container" type="javax.sql.DataSource" maxActive="400" maxIdle="20" maxWait="5000" username="scott" password="tiger" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@127.0.0.1:1521:ORCL" />指定数据源（web.xml）在项目的 web.xml中配置 <resource-ref> <res-ref-name>student</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>用数据源通过数据库获取 Connection 更改连接对象Connection的获取方式传统JDBC方式connection = DriverManager.getConnection(URL,USERNAME,PASSWORD);数据源方式Context ctx = new InitialContext() ;//context.xml DataSource ds = (DataSource)ctx.lookup("java:comp/env/student") ; connection = ds.getConnection();DBCP连接池用连接池的核心：将连接的指向改了，现在指向的是数据源而不是数据库。BasicDataSource方式（硬编码）DasicDataSource对象设置各种数据 //获取dbcp方式的ds对象 public static DataSource getDataSourceWIthDBCP(){ BasicDataSource dbcp = new BasicDataSource(); dbcp.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dbcp.setUrl("jdbc:oracle:thin:@127.0.0.1:1521:ORCL"); dbcp.setUsername("scott"); dbcp.setPassword("tiger"); dbcp.setInitialSize(20); dbcp.setMaxActive(10); return dbcp; }BasicDataSourceFactory方式配置方式（.properties文件, 编写方式key=value）dbcpconfig.properties public static DataSource getDataSourceWIthDBCPByProperties() throws Exception{ DataSource dbcp = null ; Properties props = new Properties(); InputStream input = new DBCPDemo().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties"); props.load(input); dbcp = BasicDataSourceFactory.createDataSource(props); return dbcp; }C3P0两种方式：硬编码，配置文件 合二为一 ，通过ComboPooledDataSource的构造方法参数区分：如果无参，硬编码；有参，配置文件。无参，硬编码 ComboPooledDataSource c3p0 = new ComboPooledDataSource(); c3p0.set...();有参，配置文件(c3p0-config.xml) public static DataSource getDataSourceWithC3P0() { ComboPooledDataSource c3p0 = new ComboPooledDataSource(); try { c3p0.setDriverClass("oracle.jdbc.driver.OracleDriver"); } catch (PropertyVetoException e) { e.printStackTrace(); } c3p0.setJdbcUrl("jdbc:oracle:thin:@127.0.0.1:1521:ORCL"); c3p0.setUser("scott"); c3p0.setPassword("tiger"); return c3p0; } public static DataSource getDataSourceWithC3P0ByXml() { ComboPooledDataSource c3p0 = new ComboPooledDataSource("name"); return c3p0; }Apache DBUtilsDbUtils是一个小型的类库，它设计用于使JDBC编程更加方便、简单。JDBC编程中，资源的关闭是显式的，极易导致编程错误，DbUtils把这些清理工作抽象出来，使得程序员编程时仅需要关心真正需要关心的，即：query和update数据。没详细学，以后填坑自定义标签编写标签处理类传统方式（JSP1.1）：实现 javax.servlet.jsp.tagext.Tag接口 doStartTag()简单方式（JSP2.0）：实现 javax.servlet.jsp.tagext.SimpleTag接口 doTag()如果jsp在编译阶段 发现了自定义标签 <xx:yy>,就会交给 doStartTag()或 doTag()编写标签描述符tld<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>标签库描述</description> <tlib-version>1.0</tlib-version> <short-name>xxx</short-name> <uri>标签库的uri</uri> <tag> <description>标签描述</description> <name>标签名</name> <tag-class>标签的处理</tag-class> <body-content>标签提的类型</body-content> </tag> </taglib>写标签处理类导入并使用位置：myTag.tld导入WEB-INF或子目录下WEB-INF/abc/ (特例排除，不能是WEB-INF/lib、WEB-INF/classes)使用：引入具体要使用的.tld文件 <%@ taglib uri="..." prefix="d" %>uri：每个tld文件的唯一描述符prefix：使用tld标签时的前缀当JSP容器（Tomcat、jetty）在将.jsp翻译成.servlet(.java)的时候 ,如果遇到JSP中有标签，就会依次执行setPageContext() setParent() doStartTag() doEndTag() realease(),用于解析标签的执行逻辑。传统方式和简单方式。简单方式：public class LoginTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { //判断是否已经登录成功 PageContext pageContext = (PageContext)getJspContext() ; HttpSession session = pageContext.getSession(); String name = (String)session.getAttribute("name") ; if(name !=null){ //执行一次标签体 getJspBody().invoke(null) ; } } }简单标签方式，获取JSP内置对象：getJspContext -> JspContext -> 转成子类PageContext -> PageContext就是所有JSP内置对象的入口，即可以获取一切JSP内置对象。参考链接：https://blog.csdn.net/wenwen091100304/article/details/48035003
过滤器@WebFilter(urlPatterns = "/loginfolder/*") public class filterUser implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { javax.servlet.http.HttpServletRequest req = (javax.servlet.http.HttpServletRequest) servletRequest; javax.servlet.http.HttpSession session = req.getSession(); if (session.getAttribute("user") != null) { // System.out.print(session.getAttribute("user")); // 放行 filterChain.doFilter(servletRequest, servletResponse); } else { ((javax.servlet.http.HttpServletResponse) servletResponse).sendRedirect("../index.jsp"); } } @Override public void destroy() { } } @WebFilter注解以下所有属性均为可选属性，但是 value、urlPatterns、servletNames 三者必需至少包含一个，且 value 和 urlPatterns 不能共存，如果同时指定，通常忽略 value 的取值。@WebFilter的常用属性属性名类型描述filterNameString指定过滤器的 name 属性，等价于 <filter-name>valueString[]该属性等价于 urlPatterns 属性。但是两者不应该同时使用。urlPatternsString[]指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签。servletNamesString[]指定过滤器将应用于哪些 Servlet。取值是 @WebServlet 中的 name 属性的取值，或者是 web.xml 中 <servlet-name> 的取值。dispatcherTypesDispatcherType指定过滤器的转发模式。具体取值包括：ASYNC、ERROR、FORWARD、INCLUDE、REQUEST。initParamsWebInitParam[]指定一组过滤器初始化参数，等价于 <init-param> 标签。asyncSupportedboolean声明过滤器是否支持异步操作模式，等价于 <async-supported> 标签。descriptionString该过滤器的描述信息，等价于 <description> 标签。displayNameString该过滤器的显示名，通常配合工具使用，等价于 <display-name> 标签。dispatcher请求方式： REQUEST：拦截HTTP请求 get postFORWARD：只拦截通过请求转发方式的请求 INCLUDE:只拦截拦截通过 request.getRequestDispatcher("").include()、通过 <jsp:include page="..." />此种方式发出的请求 ERROR：只拦截 <error-page>发出的请求过滤器链可以配置多个过滤器，过滤器的先后顺序 是由 <filter-mapping>的位置决定监听器public class OnlineListener implements HttpSessionAttributeListener{ //编写添加和删除属性方法 public void attributeAdded(HttpSessionBindingEvent e) { UserList.addUser(String.valueOf(e.getValue())); System.out.println("session(" + e.getSession().getId() + ")增加属性" + e.getName() + "，值为" + e.getValue()); } public void attributeRemoved(HttpSessionBindingEvent e) { UserList.removeUser(String.valueOf(e.getValue())); System.out.println("session(" + e.getSession().getId() + ")移除属性" + e.getName() + "，值为" + e.getValue()); } //编写更改属性方法 public void attributeReplaced(HttpSessionBindingEvent e) { String oldValue=String.valueOf(e.getValue()); String newValue= String.valueOf(e.getSession(). getAttribute(e.getName())); UserList.removeUser(oldValue); UserList.addUser(newValue); System.out.println("session(" + e.getSession().getId() + ")属性" + e.getName() + "的值，由"+oldValue+"更改为" + newValue); } //编写操作监听方法,Http会话的创建监听 , public void sessionCreated(HttpSessionEvent e) { System.out.println("创建新会话session("+e.getSession().getId()+")"); } //Http会话的销毁监听 public void sessionDestroyed(HttpSessionEvent e) { System.out.println("销毁会话session("+e.getSession().getId()+")"); } //编写操作上下文方法,监听ServletContext创建 public void contextInitialized(ServletContextEvent e) { System.out.println("ServletContext初始化......"); } //监听ServletContext销毁 public void contextDestroyed(ServletContextEvent e) { System.out.println("ServletContext被销毁......"); } }钝化/活化钝化/序列化/持久化：将Session内存->硬盘Session对象的四种状态Session.setAttribute("a", value)将对象a 绑定到Session中Session.removeAttribute("a")将对象a从Session中 解绑(监听绑定和解绑 HttpSessionBindingListener)钝化活化(监听钝化活化 HttpSessionActivationListener)绑定/解绑package com.demo.listener; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; @WebListener public class BeanListener implements HttpSessionBindingListener { @Override public void valueBound(HttpSessionBindingEvent event) { // this表示a对象 System.out.println("绑定对象ID:" + event.getSession().getId()); } @Override public void valueUnbound(HttpSessionBindingEvent event) { System.out.println("解绑对象ID:" + event.getSession().getId()); } }<% BeanListener BL = new BeanListener(); session.setAttribute("a", BL); %>钝化/活化配置 tomcat安装目录 /conf/context.xml <Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="5"> <Store className="org.apache.catalina.session.FileStore" directory="lq"/> </Manager>@WebListener public class Listener implements HttpSessionActivationListener { @Override public void sessionWillPassivate(HttpSessionEvent se) { // 钝化之前 // 这个对象会随着Session对象钝化而钝化 System.out.println("钝化前：" + se.getSession().getId()); } @Override public void sessionDidActivate(HttpSessionEvent se) { // 活化之后 System.out.println("活化后：" + se.getSession().getId()); } }钝化和活化本质就是序列化和反序列化，而序列化和反序列化需要实现 Serializable接口[scode type="share"]HttpSessionActivationListener只是负责在session钝化和活化的时候加以监听，钝化实际执行是通过context.xml中进行配置而进行的；活化则是在获取session中某个对象时，如果该对象在内存中不存在，则会尝试从之前钝化的文件中去获取。钝化和活化可以解决太多客户端请求服务器造成session对象大量创建占用过多内存的问题，session对象太多可以将session从内存钝化到磁盘中来减轻压力；也可以解决服务器关闭重启，session丢失的问题，钝化后重启就不怕session丢失了。[/scode]Ajax[scode type="share"]AJAX = Asynchronous JavaScript and XML（异步的 JavaScript 和 XML）。AJAX 不是新的编程语言，而是一种使用现有标准的新方法。AJAX 最大的优点是在不重新加载整个页面的情况下，可以与服务器交换数据并更新部分网页内容。AJAX 不需要任何浏览器插件，但需要用户允许JavaScript在浏览器上执行。[/scode]全局刷新：请求转发/重定向异步刷新：只刷新需要修改的地方，而页面中的其他地方保持不变。（点赞）JS实现 jQuery实现 $(function(){ //请求参数 var list = {}; //还可以使用$.post $.get $.ajax({ //请求方式 type : "POST", //请求的媒体类型 contentType: "application/json;charset=UTF-8", //请求地址 url : "http://127.0.0.1/admin/list/", //数据，json字符串 data : JSON.stringify(list), //请求成功 success : function(result) { console.log(result); }, //请求失败，包含具体的错误信息 error : function(e){ console.log(e.status); console.log(e.responseText); } }); });参考链接：https://blog.csdn.net/wxwzy738/article/details/8615244https://www.jianshu.com/p/c03be582a1d0https://blog.csdn.net/xianhenyuan/article/details/92669817
EL表达式传统JSP中用Java代码显示数据的弊端：类型转换，需要处理 null目的：消除jsp中的Java代码语法：${EL表达式}EL不需要导包在EL中调用属性，其实是调用的getXxx()方法${范围.对象.属性.属性的属性}. ：使用方便[] ：如果是常量属性，需要使用双引号/单引号引起来;比点操作符更加强大[]优点：可以容纳一些特殊符号 ". ? -"[]可以容纳变量属性（可以动态赋值）String x = "a"; ${requestScope.a}等价于${requestScope["a"]}等价于${${requestScope[x]}可以处理数组 ${requestScope.arr[0]}EL隐含对象Scope对象(EL域对象)pageScope，requestScope，sessionScope，applicationScope变量用来访问存储在各个作用域层次的变量。如果不指定域对象，则默认根据从小到大顺序依次取值。param和paramValues对象param对象返回单一的字符串，而paramValues对象则返回一个字符串数组。$(param.name)$(paramValues.name[0])pageContext对象pageContext对象是JSP中pageContext对象的引用。通过pageContext对象，可以访问JSP中所有隐式对象。${pageContext} ${pageContext.request} ${pageContext.sessoin} ${pageContext.request.serverPort}方法名去掉括号和get并且首字母小写JSTL需要引入jar ：jstl.jarMaven配置 <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>引入tablib <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>在IDEA中配置Maven，在jsp前加上 <%@ page isELIgnored="false"%>才能正确解析EL表达式核心标签格式化标签SQL 标签XML 标签JSTL 函数核心标签<c:set>赋值I. 在某个作用域之中（4个范围对象），给某个变量赋值 <%-- request.setAttribute("name", "zhangsan") ; --%> <c:set var="name" value="zhangsan" scope="request"/> ${requestScope.name }<c:set var="变量名" value="变量值" scope="4个范围对象的作用域"/>II. 给普通对象赋值在某个作用域之中（4个范围对象），给某个对象的属性复制 （此种写法，不能指定scope属性） <c:set target="${requestScope.student}" property="sname" value="zxs" />III. 给map对象赋值 <c:set target="${requestScope.countries}" property="cn" value="中国" /><c:set target="对象" property="对象的属性" value="赋值" />注意 <c:set>可以给不存在的变量赋值 （但不能给不存在的对象赋值）<c:out>显示true:<c:out value='百度' default="当value为空的，显示的默认值" escapeXml="true" /> false： <c:out value='百度' escapeXml="false" />显示如下：<c:remove >删除属性<c:remove var="a" scope="request"/>删除request域中删除a变量选择语句<c:if test="${1>0}" scope="request" var="result"> true </c:if>result存储判断的bool值多重if：<c:choose> <c:when test=""></c:when> <c:when test=""></c:when> <c:otherwise></c:otherwise> </c:choose>在使用 test=""一定要注意后面是否有空格迭代<c:forEach begin="0" end="5" step="1" varStatus="status"> ${status.index} </c:forEach><c:forEach var="name" items="${集合}"> ${name} </c:forEach>格式化标签JSTL格式化标签用来格式化并输出文本、日期、时间、数字。SQL 标签JSTL SQL标签库提供了与关系型数据库（Oracle，MySQL，SQL Server等等）进行交互的标签。XML 标签JSTL XML标签库提供了创建和操作XML文档的标签。JSTL 函数JSTL包含一系列标准函数，大部分是通用的字符串处理函数。参考链接：https://www.runoob.com/jsp/jsp-jstl.html
Begin反射机制是在运行状态中:对于任意一个类，都能够知道这个类的所有属性和方法;对于任意一个对象，都能够调用它的任意一个方法和属性;反射提供的功能：在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。获取反射对象package reflect; public class reflectDemo1 { public static void main(String[] args) { // 获取反射对象（反射入口） // 1. Class.forName(全类名) try { Class<?> perClazz1 = Class.forName("reflect.Person"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 2. 类名.class Class<?> perClazz2 = reflect.Person.class; // 3. 对象.getClass() Person per = new Person(); Class<?> perClazz3 = per.getClass(); } }package reflect; public class Person implements MyInterface { public Person() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public static void staticMethod() { System.out.println("staticMethod"); } public Person(int id) { this.id = id; } public Person(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } private void privateMethod() { System.out.println("privateMethod"); } private void privateMethod(String s) { System.out.println(s); } private int id; private String name; private int age; @Override public void interfaceMethod() { System.out.println("interfaceMethod"); } }Java泛型中的通配符<?>称为非受限通配，其实就相当于 <? extends Object>，可以表示任何一种对象类型<? extends T>称为受限通配，表示需要的是T或者T的子类型<? super T>称为下限通配，表示需要的是T的父类型断言assert <boolean表达式>如果<boolean表达式>为 true，则程序继续执行。如果为 false，则程序抛出 AssertionError，并终止执行。assert <boolean表达式> : <错误信息表达式> 如果 <boolean表达式>为 true，则程序继续执行。如果为 false，则程序抛出 java.lang.AssertionError，并输入 <错误信息表达式>。 <错误信息表达式>表示一个基本类型、表达式或者是一个 Object，用于在失败时输出错误信息。获取Class的内容 // Class入口 Class<?> perClazz1 = null; try { perClazz1 = Class.forName("reflect.Person"); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 获取所有公共方法(本类及父类，接口中的方法且符合访问修饰符规则) assert perClazz1 != null; Method[] methods = perClazz1.getMethods(); for (Method method : methods) { System.out.println(method); } // 获取所有接口 Class<?>[] interfaces = perClazz1.getInterfaces(); for (Class<?> inter : interfaces) { System.out.println(inter); } // 获取所有父类(单继承) Class<?> superclass = perClazz1.getSuperclass(); System.out.println(superclass); // 获取所有构造方法 Constructor<?>[] constructors = perClazz1.getConstructors(); // 获取所有公共属性(包括父类，符合访问修饰符) Field[] fields = perClazz1.getFields(); // 获取当前类的所有方法(当前类，包括private) Method[] methods1 = perClazz1.getDeclaredMethods(); // 获取当前反射所代表类（接口）的对象的实例 Object instance = null; try { instance = perClazz1.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } Person per = (Person) instance; assert per != null; per.interfaceMethod();操作对象 // 操作获取实例的对象 per.setAge(6); per.setName("zs"); // 操作属性 Field idField = perClazz1.getDeclaredField("id"); // 修改属性的访问权限 idField.setAccessible(true); idField.set(per, 1); // 操作方法 Method method = perClazz1.getDeclaredMethod("privateMethod", null); method.setAccessible(true); method.invoke(per, null); // 有参 method = perClazz1.getDeclaredMethod("privateMethod", String.class); method.setAccessible(true); method.invoke(per, "zs");操作构造方法 // 操作构造方法 Constructor<?>[] constructors1 = perClazz1.getDeclaredConstructors(); for(Constructor<?> constructor: constructors1) System.out.println(constructor); // 获取指定构造方法(反射中基本类型和包装类型不同) // 如果是private要用perClazz1.getDeclaredConstructor() Constructor<?> constructor = perClazz1.getConstructor(int.class); System.out.println(constructor); // 产生对象 Person instance1 = (Person) constructor.newInstance(10);通过反射运行配置文件内容配置文件class.txt：className = reflect.Student methodName = staticMethod Properties prop = new Properties(); prop.load(new FileReader("class.txt")); String classname = prop.getProperty("classname"); String methodname = prop.getProperty("methodname"); Class<?> perClazz = null; try { perClazz = Class.forName(classname); } catch (ClassNotFoundException e) { e.printStackTrace(); } Method method = perClazz.getMethod(methodname); method.invoke(perClazz.newInstance());越过泛型检查 ArrayList<Integer> list = new ArrayList<>(); list.add(123); list.add(3); list.add(2); // list.add("zs") ; Class<?> listClazz = list.getClass(); Method method = listClazz.getMethod("add", Object.class); method.invoke(list, "zs"); System.out.println(list);通用的set方法public class PropertyUtil { public static void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Class<?> clazz = obj.getClass(); Field field = clazz.getDeclaredField(propertyName); field.setAccessible(true); field.set(obj, value); } }调用： Person per = new Person(); PropertyUtil.setProperty(per, "name", "zs");动态一维数组 Scanner in = new Scanner(System.in); System.out.println("Input Type:"); String type = in.nextLine(); System.out.println("Input num:"); int num = in.nextInt(); Class<?> c = Class.forName(type); Object arr = Array.newInstance(c, num); Array.set(arr, 0, "zs"); System.out.println(Array.get(arr, 0));动态二维数组 Class c = Integer.class; int[] dim = {4, 4}; Object o = Array.newInstance(c, dim); // 第二行 Object arr2 = Array.get(o, 2); // 第二行第一列赋值 Array.set(arr2, 1, 666); System.out.println(Array.get(arr2, 1));
MVC设计模式M：Mode，模型：一个功能。用JavaBean实现。V：View，视图：用于展示、以及与用户交互。使用html js css jsp jquery等前端技术实现。C：Controller，控制器：接受请求，将请求跳转到模型进行处理。模型处理完毕后，再将处理的结果返回给请求处。可以用jsp实现，但是一般建议使用Servlet实现控制器。ServletJava类符合以下规范：必须继承 javax.servlet.http.HttpServlet重写其中的 doGet()或 doPost()方法doGet()：接受并处理所有get提交方式的请求doPost()：接受并处理所有post提交方式的请求配置Serlvet2.5需要配置 web.xml <servlet> <servlet-name>WelcomeServlet</servlet-name> <servlet-class>org.lanqiao.servlet.WelcomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>WelcomeServlet</servlet-name> <url-pattern>/WelcomeServlet</url-pattern> </servlet-mapping>Servlet执行流程：请求 -> <url-pattern> -> 根据 <servlet-mapping>中的 <servlet-name> 去匹配 <servlet> 中的 <servlet-name> 然后寻找到 <servlet-class> 中将请求交由该 <servlet-class>执行。手工创建Servlet创建一个类，继承 HttpServlet重写 doGet()、doPost()方法配置 web.xml中的Servlet映射关系Servlet3.0+要在Servlet类的定义处之上编写注解@WebServlet("url-pattern的值")匹配流程：请求地址与@WebServlet中的值进行匹配，如果匹配成功，则说明请求的就是该注解所对应的类。@WebServlet(name = "HelloServlet", urlPatterns = "/HelloServlet") public class HelloServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doPost"); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("doGet"); } }Servlet注解配置细节属性名类型描述nameString指定Servlet 的 name 属性，等价于 <servlet-name>。如果没有显式指定，则该 Servlet 的取值即为类的全限定名。valueString[]该属性等价于 urlPatterns 属性。两个属性不能同时使用。urlPatternsString[]指定一组 Servlet 的 URL 匹配模式。等价于 <url-pattern>标签。loadOnStartupint指定 Servlet 的加载顺序，等价于 <load-on-startup>标签。initParamsWebInitParam[]指定一组 Servlet 初始化参数，等价于 <init-param>标签。asyncSupportedboolean声明 Servlet 是否支持异步操作模式，等价于 <async-supported>标签。descriptionString该 Servlet 的描述信息，等价于 <description>标签。displayNameString该 Servlet 的显示名，通常配合工具使用，等价于 <display-name>标签。注意字符串要有 /，可以只传一个参数，默认为 value路径web.xml中的 /：代表项目根路径 (ip:port/projec)jsp中的 /：服务器根路径 (ip:port)Servlet生命周期5个阶段加载初始化：init()，该方法会在 Servlet被加载并实例化的以后 执行服务：service() 通过doGet()，doPost()销毁：destroy()，Servlet被系统回收时执行卸载init(): a.默认第一次访问Servlet时会被执行 （只执行这一次） b.可以修改为Tomcat启动时自动执行 i.Servlet2.5： web.xml <servlet> ... <load-on-startup>1</load-on-startup> </servlet> 其中的“1”代表第一个执行。 ii.Servlet3.0 @WebServlet( value="/WelcomeServlet" ,loadOnStartup=1 ) service()调用doGet()/doPost：调用几次，执行几次 destroy()：关闭tomcat服务时，执行一次。HttpServletRequest中的方法：(同request)，例如setAttrite()、getCookies()、getMethod()HttpServletResponse中的方法：(同response)获取常见内置对象out: PrintWriter out = response.getWriter();session: request.getSession();application: request.getServletContext();三层架构UI(表现层): 主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数据。位于 xxx.servlet包中。BLL:(业务逻辑层): UI层和DAL层之间的桥梁。实现业务逻辑。业务逻辑具体包含：验证、计算、业务规则等等。位于 xxx.servic/manager/bll包中DAL:(数据访问层): 与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层，同时将业务层处理的数据保存到数据库。（当然这些操作都是基于UI层的。用户的需求反映给界面（UI），UI反映给BLL，BLL反映给DAL，DAL进行数据的操作，操作后再一一返回，直到将用户所需数据反馈给用户）位于 xxx.dao包中三层架构与MVC表示层：V前端，C后台业务逻辑层，数据访问层（原子性），实体类：M三层优化加入接口面向接口开发：先创建接口-再实现类service、dao加入接口接口与实现类的命名规范接口：interface， 起名 I实体类Service IStudentService IStudentDao 实现类：implements 起名 实体类ServiceImpl StudentServiceImpl StudentDaoImpl 接口：I实体类层所在包名 IStudentService、IStudentDao 接口所在的包：xxx.service xx.dao 实现类：实体类层所在包名Impl StudentServiceImpl、StudentDaoImpl 实现类所在的包：xxx.service.impl xxx.dao.impl以后使用接口/实现类时，推荐写法：接口 x = new 实现类(); IStudentDao studentDao = new StudentDaoImpl();DBUtil 通用的数据库帮助类，可以简化Dao层的代码量帮助类 一般建议写在 xxx.util包dao和DBUtil的区别：dao是处理特定类的数据库操作类，DBUtil是通用数据库操作类参考链接：https://blog.csdn.net/qq_35038153/article/details/77484185https://www.runoob.com/w3cnote/three-tier-architecture.html
前言Java基础语法的坑补上了，开始学JSP啦希望头发不会少...QWQBeginJSP页面元素<%局部变量、java语句%><%!全局变量、定义方法%><%=输出表达式 %>page指令page指定的属性：language:jsp页面使用的脚本语言import:导入类pageEncoding:jsp文件自身编码contentType:浏览器解析jsp的编码<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>注释html注释 ,用户可通过浏览器查看源码查看。java注释 //， /*...*/jsp注释 <%-- --%>常见状态码200：一切正常300/301: 页面重定向 （跳转）404: 资源不存在403：权限不足 （如果访问a目录，但是a目录设置 不可见）500：服务器内部错误（代码有误）设置初始页面在项目/WEB-INF/web.xml中设置 默认的 初始页面<welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list>虚拟路径方法一：将web项目配置到webapps以外的目录在conf/server.xml中配置host标签中：<Context docBase="D:\study\JspProject" path="/JspProject" />docBase：实际路径path：虚拟路径 （绝对路径、相对路径（相对于webapps））重启方式二：在apacheconfCatalinalocalhost中新建“项目名.xml”中新增一行：<Context docBase="D:\study\JspProject" path="/JspProject" />虚拟主机通过www.test.com访问本机方法一：修改 conf/server.xml <Engine name="Catalina" defaultHost="www.test.com"> <Host appBase="D:\study\JspProject" name="www.test.com"> <Context docBase="D:\study\JspProject" path="/"/> </Host>方法二：在C:WindowsSystem32driversetchost增加127.0.0.1 www.test.com内置对象out输出对象，向客户端输出内容request请求对象；存储“客户端向服务端发送的请求信息”常见方法：String getParameter(String name) :根据请求的字段名key （input标签的name属性值） ，返回字段值value （input标签的value属性值）String[] getParameterValues(String name): 根据请求的字段名key ，返回多个字段值value （checkbox）void setCharacterEncoding("编码格式utf-8") ：设置post方式的请求编码 （tomcat7以前默认iso-8859-1，tomcat8以后改为了utf-8）getRequestDispatcher("b.jsp").forward(request,response) ; ：请求转发 的方式跳转页面 A - > BServletContext getServerContext():获取项目的ServletContext对象注意：get与post请求方式的区别：get方式 在地址栏显示 请求信息 (但是地址栏能够容纳的 信息有限，4-5KB；如果请求数据存在大文件，图片等 会出现地址栏无法容纳全部的数据而出错)，post不会显示文件上传操作，必须是post修改server.xml ，一次性的 更改tomcat默认get提交方式的编码 （utf-8）建议 使用tomcat时，首先在server.xml中统一get方式的编码 URIEncoding="UTF-8"推荐使用post。response：响应对象常见方法：void addCookie( Cookie cookie ); 服务端向客户端增加cookie对象void sendRedirect(String location ) throws IOException; :页面跳转的一种方式（重定向）void setContetType(String type):设置服务端响应的编码（设置服务端的contentType类型） 请求转发重定向地址栏是否改变不变改变是否保留第一次请求数据保留不保留请求次数12跳转发生位置服务端客户端发出的第二次跳转session会话存在于服务端，cookie由服务端生成，发送给客户端保存，不是内置对象，需要 newcookie：K-V对作用：提高访问服务端的效率，但是安全性较差。javax.servlet.http.Cookie提供public Cookie(String name,String value)String getName()：获取nameString getValue():获取valuevoid setMaxAge(int);最大有效期（秒）如何使用cookie：服务端准备Cookie：response.addCookie(Cookie cookie)页面跳转（转发，重定向）客户端获取cookie: request.getCookies();服务端增加cookie通过response对象客户端获取对象通过request对象不能直接获取某一个单独对象，只能一次性将 全部的cookie拿到建议cookie只保存英文数字，否则需要进行编码、解码 request.setCharacterEncoding("utf-8"); Cookie[] cookies = request.getCookies(); Cookie cookie = new Cookie(); cookie.setMaxAge(30 * 24 * 60 * 60); //cookie.setPath("/"); response.addCookie(cookie); for (Cookie tmp : cookies) { if (tmp.getName().equals(name)) { } }sessionsession机制：客户端第一次请求服务端时，（jsessionid-sessionid匹配，不存在）服务端会产生一个session对象（用于保存该客户的信息）。并且每个session对象 都会有一个唯一的 sessionId( 用于区分其他session);服务端由会产生一个cookie，并且该cookie的name=JSESSIONID ,value=服务端sessionId的值；然后服务端会在响应客户端的同时将该cookie发送给客户端，至此客户端就有了一个cookie(JSESSIONID)；因此，客户端的cookie就可以和服务端的session一一对应（JSESSIONID - sessionID）客户端第二/n次请求服务端时: 服务端会先用客户端cookie的JSESSIONID 去服务端的session中匹配sessionid,如果匹配成功（cookie jsessionid和sesion sessionid），说明此用户不是第一次访问，无需登录；session方法：String getId() :获取sessionIdboolean isNew() :判断是否是新用户（第一次访问）void invalidate():使session失效（退出登录、注销）void setAttribute()Object getAttribute()void setMaxInactiveInterval(秒) ：设置最大有效非活动时间int getMaxInactiveInterval():获取最大有效非活动时间cookie和session的区别 sessioncookie保存的位置服务端客户端安全性较安全较不安全保存的内容ObjectStringapplication全局对象String getContextPath() 获取虚拟路径String getRealPath(String name): 获取绝对路径（虚拟路径相对的绝对路径）application.setAttribute(k, v)application.getAttribute(k)pageContextJSP页面容器config配置对象（服务器配置信息）page当前JSP页面对象（相当于Java中的this）exception异常对象四大范围对象小->大对象作用范围pageContext JSP页面容器（有时称为page对象）当前页面有效request 请求对象同一次请求有效session 会话对象同一次会话有效appliation 全局对象全局有效（整个项目有效）以上4个对象共有的方法：Object getAttribute(String name):根据属性名，或者属性值void setAttribute(String name,Object obj) :设置属性值（新增，修改）setAttribute("a","b")：如果a对象之前不存在，则新建一个a对象。如果a之前已经存在，则将a的值改为bvoid removeAttribute(String name)：根据属性名，删除对象范围详解pageContext 当前页面有效 (页面跳转后无效)request 同一次请求有效；其他请求无效 （请求转发后有效；重定向后无效）session 同一次会话有效 （无论怎么跳转，都有效；关闭/切换浏览器后无效 ； 从 登陆->退出 之间 全部有效）application 全局变量；整个项目运行期间 都有效 (切换浏览器 仍然有效)；关闭服务、其他项目无效如果需要多个项目共享、重启后仍然有效：JNDI以上的4个范围对象，通过 setAttribute()赋值，通过 getAttribute()取值。以上范围对象，尽量使用最小的范围。因为对象的范围越大，造成的性能损耗越大。