Your SlideShare is downloading. ×
0
JavaTWO  專業技術大會 Java Annotation 李日貴 (jini)
About Me <ul><li>jini@JWorld.TW ( jakarta99 ) </li></ul><ul><li>SoftLeader Tech. Corp. President since 2002 </li></ul><ul>...
Agenda <ul><li>Introduction </li></ul><ul><li>Servlet 3.0 </li></ul><ul><li>EJB 3.1 </li></ul><ul><li>WebServices </li></u...
@Annotation Course1 Introduction
What’s annotation <ul><li>Begin with “@”  </li></ul><ul><li>An Interface </li></ul><ul><li>Set Values </li></ul><ul><ul><l...
RetentionPolicy
@Annotation Runtime <ul><li>Default value </li></ul><ul><li>Stereotype </li></ul><ul><ul><li>Find the classes contain @Ann...
 
@Target(ElementType) <ul><li>@AnnoTarget( TYPE ) </li></ul><ul><li>public class TheTarget { </li></ul><ul><li>@AnnoTarget(...
@Annotation Course 2  Servlet 3.0
Servlet 2.5 <ul><li>package  javatwo.annotation ; </li></ul><ul><li>public class  CatServlet  extends HttpServlet { </li><...
Servlet 3.0 <ul><li>package  javatwo.annotation ; </li></ul><ul><li>@WebServlet(“/cat”) </li></ul><ul><li>public class  Ca...
Servlet 3.0 <ul><li>No web.xml needed </li></ul><ul><ul><li>@WebServlet - Define a Servlet </li></ul></ul><ul><ul><li>@Web...
FileUpload in Servlet 3.0 <ul><li>@WebServlet(“/upload”) </li></ul><ul><li>@MultipartConfig(location=“c:tmp”) </li></ul><u...
set Async in Servlet 3.0 <ul><li>@WebServlet(name=“AsyncServlet”, urlPatterns=“/async”,  asyncSupported=“true”) </li></ul>...
@Annotation Course 3  EJB 3.1
EJB 2.x <ul><li>public interface CalculatorLocal extends EJBLocalObject {..} </li></ul><ul><li>public interface Calculator...
EJB 3.0 <ul><li>@Local  public interface CalculatorLocal  extends EJBLocalObject  {..} </li></ul><ul><li>public interface ...
EJB 3.1 (LocalBean) <ul><li>@Local  public interface CalculatorLocal extends EJBLocalObject {..} </li></ul><ul><li>public ...
Singleton SessionBean <ul><li>@Singleton </li></ul><ul><li>@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) </l...
Schedule Timer <ul><li>@Singleton </li></ul><ul><li>public class TransFileBean { </li></ul><ul><li>@Schedule(minute=“0”,ho...
@Annotation Course 4  WebServices
WebServices <ul><li>“ Big” WebServices </li></ul><ul><ul><li>SOAP </li></ul></ul><ul><ul><li>JAX-WS </li></ul></ul><ul><ul...
SOAP WebService <ul><li>import  javax.jws. WebMethod; </li></ul><ul><li>import  javax.jws. WebService; </li></ul><ul><li>@...
 
RESTful WebService <ul><li>import javax.ws.rs.GET; </li></ul><ul><li>import javax.ws.rs.Path; </li></ul><ul><li>import jav...
 
@Annotation Course 5  CDI
Resource Injection JEE5 <ul><li>@WebServlet(“/product”) </li></ul><ul><li>public ProductServlet extends HttpServlet { </li...
Ambiguous Inject <ul><li>@Qualifier </li></ul><ul><li>public @interface CarPolicyNo { } </li></ul><ul><li>@Qualifier </li>...
@Produces <ul><li>public class DatabaseProducer { </li></ul><ul><li>@Produces  @PersistenceContext(unitName=“ds1”) </li></...
@Annotation Course 6  Opensources
jUnit 3.x <ul><li>import junit.framework.Assert; </li></ul><ul><li>import junit.framework.TestCase; </li></ul><ul><li>publ...
jUnit 4.x <ul><li>public class CalculateServiceTest4  extends TestCase  { </li></ul><ul><li>@BeforeClass </li></ul><ul><li...
 
Spring 2.5 <ul><li><beans> </li></ul><ul><li><bean id=“newsDao” class=“javatwo.annotation.spring.NewsDao”/> </li></ul><ul>...
Spring 3.0 <ul><li>@Named </li></ul><ul><li>public class NewsService { </li></ul><ul><li>@Inject </li></ul><ul><li>private...
Spring 3.0 <ul><li>@Configuration </li></ul><ul><li>public class AppConfig { </li></ul><ul><li>private  @Value(“#{sysprop....
MVC in Spring3.0 <ul><li>@Controller </li></ul><ul><li>@RequestMapping(“/news”) </li></ul><ul><li>public class NewsControl...
@Annotation Course 7  Javassist / ASM
Reflection <ul><li>Class myClass = MyClass.class; </li></ul><ul><li>Method[] myMethods = myClass. getMethods() ; </li></ul...
Scan in ClassLoader <ul><li>scannotation.sf.net project ( based on javassist ) </li></ul><ul><li>URL[] urls = ClasspathUrl...
Get the Annotation values <ul><li>ClassPool pool = ClassPool.getDefault(); </li></ul><ul><li>pool.insertClassPath(new Clas...
Class R/W in ASM <ul><li>public class  ScanMyDocVisitor   implements   ClassVisitor  { </li></ul><ul><li>public  Annotatio...
 
Upcoming SlideShare
Loading in...5
×

比XML更好用的Java Annotation

2,745

Published on

大綱
- Java Annotation 基本概念
- JavaEE6 annotation
- 實戰應用 Java annotation

Published in: Technology, News & Politics
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
2,745
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
123
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "比XML更好用的Java Annotation"

  1. 1. JavaTWO 專業技術大會 Java Annotation 李日貴 (jini)
  2. 2. About Me <ul><li>jini@JWorld.TW ( jakarta99 ) </li></ul><ul><li>SoftLeader Tech. Corp. President since 2002 </li></ul><ul><li>Focus on dev in Java enterprise applications for Insurances, Banking and Betting. </li></ul><ul><li>Researching and Sharing Java Opensources </li></ul><ul><li>My Google+ ( http://gplus.to/jakarta99 ) </li></ul><ul><li>Single now </li></ul>
  3. 3. Agenda <ul><li>Introduction </li></ul><ul><li>Servlet 3.0 </li></ul><ul><li>EJB 3.1 </li></ul><ul><li>WebServices </li></ul><ul><li>CDI </li></ul><ul><li>Opensources </li></ul><ul><li>Javassist / ASM </li></ul>
  4. 4. @Annotation Course1 Introduction
  5. 5. What’s annotation <ul><li>Begin with “@” </li></ul><ul><li>An Interface </li></ul><ul><li>Set Values </li></ul><ul><ul><li>@Annotation </li></ul></ul><ul><ul><li>@Annotation(“someValue”) </li></ul></ul><ul><ul><li>@Annotation(var1=“someA”,var2=“someB”) </li></ul></ul><ul><ul><li>@Annotation(value={“Apple”, “Banana”, “Cherry”}) </li></ul></ul>
  6. 6. RetentionPolicy
  7. 7. @Annotation Runtime <ul><li>Default value </li></ul><ul><li>Stereotype </li></ul><ul><ul><li>Find the classes contain @Annotation </li></ul></ul><ul><ul><li>Initialize them as what you want </li></ul></ul><ul><ul><li>To generate the codes or xml and etc.. </li></ul></ul>
  8. 9. @Target(ElementType) <ul><li>@AnnoTarget( TYPE ) </li></ul><ul><li>public class TheTarget { </li></ul><ul><li>@AnnoTarget( FIELD ) </li></ul><ul><li>private String globalMsg; </li></ul><ul><li>@AnnoTarget( CONSTRUCTOR ) </li></ul><ul><li>public TheTarget() { </li></ul><ul><li>// This is a constructor </li></ul><ul><li>} </li></ul><ul><li>@AnnoTarget( METHOD ) </li></ul><ul><li>public void someMethod ( @AnnoTarget ( PARAMETER ) String myParam) { </li></ul><ul><li>// This is a method </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  9. 10. @Annotation Course 2 Servlet 3.0
  10. 11. Servlet 2.5 <ul><li>package javatwo.annotation ; </li></ul><ul><li>public class CatServlet extends HttpServlet { </li></ul><ul><li>public void doGet(HttpServletRequest request, HttpServletResponse response) </li></ul><ul><li>throws ServletException, IOException { </li></ul><ul><li>// do something </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>web.xml </li></ul><ul><li><web-app> </li></ul><ul><li><servlet> </li></ul><ul><li><servlet-name> CatServlet </servlet-name> </li></ul><ul><li><servlet-class> javatwo.annotation.CatServlet </servlet-class> </li></ul><ul><li></servlet> </li></ul><ul><li><servlet-mapping> </li></ul><ul><li><servlet-name> CatServlet </servlet-name> </li></ul><ul><li>< url-pattern >/cat</ url-pattern > </li></ul><ul><li></servlet-mapping> </li></ul><ul><li></web-app> </li></ul>
  11. 12. Servlet 3.0 <ul><li>package javatwo.annotation ; </li></ul><ul><li>@WebServlet(“/cat”) </li></ul><ul><li>public class CatServlet extends HttpServlet { </li></ul><ul><li>public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { </li></ul><ul><li>// do something </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>@WebServlet (name = &quot;catServlet&quot;, urlPatterns = &quot;/cat&quot;, </li></ul><ul><li> initParams = { @WebInitParam (name = &quot;name&quot;, value = &quot;bobo&quot;) }) </li></ul>
  12. 13. Servlet 3.0 <ul><li>No web.xml needed </li></ul><ul><ul><li>@WebServlet - Define a Servlet </li></ul></ul><ul><ul><li>@WebFilter - Define a Filter </li></ul></ul><ul><ul><li>@WebListener - Define a Listener </li></ul></ul><ul><ul><li>@WebInitParam - Define init parameter </li></ul></ul><ul><ul><li>@MultipartConfig - Define upload properties </li></ul></ul><ul><li>Can use web.xml to override values that specified by the Annotations. </li></ul>
  13. 14. FileUpload in Servlet 3.0 <ul><li>@WebServlet(“/upload”) </li></ul><ul><li>@MultipartConfig(location=“c:tmp”) </li></ul><ul><li>public class FileUploadServlet extends HttpServlet { </li></ul><ul><li>public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { </li></ul><ul><li>Collection< Part > parts = request.getParts(); </li></ul><ul><li>for( Part part:parts) { </li></ul><ul><li>part.write(“upload.txt”); // save to c:tmpupload.txt </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  14. 15. set Async in Servlet 3.0 <ul><li>@WebServlet(name=“AsyncServlet”, urlPatterns=“/async”, asyncSupported=“true”) </li></ul><ul><li>public class AsyncServlet extends HttpServlet { </li></ul><ul><li>public void doGet(HttpServletRequest request, HttpServletResponse response) { </li></ul><ul><li>AsyncContext aCtx = request.startAsync(request, response); </li></ul><ul><li>ServletContext sc = request.getServletContext(); </li></ul><ul><li>((Queue<AsyncContext>)sc.getAttribute(“slowQueue&quot;)).add(aCtx); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>@WebListener </li></ul><ul><li>public class SlowQueueListener implements ServletContextListener { </li></ul><ul><li>public void contextInitialized(ServletContextEvent sce) { … } </li></ul><ul><li>} </li></ul>
  15. 16. @Annotation Course 3 EJB 3.1
  16. 17. EJB 2.x <ul><li>public interface CalculatorLocal extends EJBLocalObject {..} </li></ul><ul><li>public interface CalculatorLocalHome extends EJBLocalHome {..} </li></ul><ul><li>public class CalculatorBean implements SessionBean { .. } </li></ul><ul><li>ejb-jar.xml </li></ul><ul><li><ejb-jar> </li></ul><ul><li><enterprise-beans> </li></ul><ul><li><session> </li></ul><ul><li><ejb-name>calculator</ejb-name> </li></ul><ul><li><local-home>CalculatorLocalHome</local-home> </li></ul><ul><li><local>CalculatorLocal</local> </li></ul><ul><li><ejb-class>CalculatorBean</ejb-class> </li></ul><ul><li><session-type>Stateless</session-type> </li></ul><ul><li><transaction-type>Container</transaction-type> </li></ul><ul><li></session> </li></ul><ul><li></enterprise-beans> </li></ul><ul><li></ejb-jar> </li></ul>
  17. 18. EJB 3.0 <ul><li>@Local public interface CalculatorLocal extends EJBLocalObject {..} </li></ul><ul><li>public interface CalculatorHome extends EJBHome {..} </li></ul><ul><li>@Stateless public class CalculatorBean implements CalculatorLocal{ .. } </li></ul><ul><li>ejb-jar.xml </li></ul>
  18. 19. EJB 3.1 (LocalBean) <ul><li>@Local public interface CalculatorLocal extends EJBLocalObject {..} </li></ul><ul><li>public interface CalculatorHome extends EJBHome {..} </li></ul><ul><li>@Stateless public class CalculatorBean implements CalculatorLocal { .. } </li></ul><ul><li>ejb-jar.xml </li></ul>
  19. 20. Singleton SessionBean <ul><li>@Singleton </li></ul><ul><li>@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) </li></ul><ul><li>public class SingletonBean { </li></ul><ul><li>private int clickCount = 0; </li></ul><ul><li>@Lock(LockType.WRITE) </li></ul><ul><li>public int getClickCount() { </li></ul><ul><li>return clickCount++; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>//Thread-Safe, Transactional </li></ul>
  20. 21. Schedule Timer <ul><li>@Singleton </li></ul><ul><li>public class TransFileBean { </li></ul><ul><li>@Schedule(minute=“0”,hour=“4”,dayOfMonth=“Last”) </li></ul><ul><li>public void trans() { </li></ul><ul><li>// do transfer </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  21. 22. @Annotation Course 4 WebServices
  22. 23. WebServices <ul><li>“ Big” WebServices </li></ul><ul><ul><li>SOAP </li></ul></ul><ul><ul><li>JAX-WS </li></ul></ul><ul><ul><li>javax.jws.* </li></ul></ul><ul><li>RESTful WebServices </li></ul><ul><ul><li>REST </li></ul></ul><ul><ul><li>JAX-RS </li></ul></ul><ul><ul><li>javax.ws.rs.* </li></ul></ul>
  23. 24. SOAP WebService <ul><li>import javax.jws. WebMethod; </li></ul><ul><li>import javax.jws. WebService; </li></ul><ul><li>@WebService(serviceName=“helloService”) </li></ul><ul><li>public class Hello { </li></ul><ul><li>@WebMethod </li></ul><ul><li>public String sayHello(String name) { </li></ul><ul><li>return “Hello “+name; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  24. 26. RESTful WebService <ul><li>import javax.ws.rs.GET; </li></ul><ul><li>import javax.ws.rs.Path; </li></ul><ul><li>import javax.ws.rs.PathParam; </li></ul><ul><li>import javax.ws.rs.Produces; </li></ul><ul><li>import javax.ws.rs.core.MediaType; </li></ul><ul><li>@Path(&quot;/helloREST/{ name }&quot;) </li></ul><ul><li>public class HelloREST { </li></ul><ul><li>@GET </li></ul><ul><li>@Produces(MediaType.TEXT_PLAIN) </li></ul><ul><li>public String getHello (@PathParam(&quot; name &quot;) String name) { </li></ul><ul><li>return &quot;Hello *&quot;+name+&quot;* from JAX-RS&quot;; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  25. 28. @Annotation Course 5 CDI
  26. 29. Resource Injection JEE5 <ul><li>@WebServlet(“/product”) </li></ul><ul><li>public ProductServlet extends HttpServlet { </li></ul><ul><li>@Inject </li></ul><ul><li>private ProductBean productBean; </li></ul><ul><li>.. </li></ul><ul><li>} </li></ul><ul><li>@Stateless </li></ul><ul><li>public class ProductBean { </li></ul><ul><li>.. </li></ul><ul><li>} </li></ul>
  27. 30. Ambiguous Inject <ul><li>@Qualifier </li></ul><ul><li>public @interface CarPolicyNo { } </li></ul><ul><li>@Qualifier </li></ul><ul><li>public @interface FirePolicyNo { } </li></ul><ul><li>@CarPolicyNoGenerator </li></ul><ul><li>public class CarPolicyNoGenerator implements PolicyNoGenerator {.. } </li></ul><ul><li>@FirePolicyNoGenerator </li></ul><ul><li>public class FirePolicyNoGenerator implements PolicyNoGenerator{ .. } </li></ul><ul><li>public class FireService { </li></ul><ul><li>@Inject </li></ul><ul><li>@FirePolicyNoGenerator </li></ul><ul><li>private PolicyNoGenerator policyNoGenerator; </li></ul><ul><li>} </li></ul>
  28. 31. @Produces <ul><li>public class DatabaseProducer { </li></ul><ul><li>@Produces @PersistenceContext(unitName=“ds1”) </li></ul><ul><li>@FireDatabase </li></ul><ul><li>private EntityManager em; </li></ul><ul><li>} </li></ul><ul><li>public class FireService { </li></ul><ul><li>@Inject @FireDatabase </li></ul><ul><li>private EntityManager em; </li></ul><ul><li>} </li></ul>
  29. 32. @Annotation Course 6 Opensources
  30. 33. jUnit 3.x <ul><li>import junit.framework.Assert; </li></ul><ul><li>import junit.framework.TestCase; </li></ul><ul><li>public class CalculateServiceTest3 extends TestCase { </li></ul><ul><li>public void setUp () throws Exception { } </li></ul><ul><li>public void test Sum() { </li></ul><ul><li>int actual = new CalculateService().sum(3,5); </li></ul><ul><li>Assert.assertEquals( 8 , actual ); </li></ul><ul><li>} </li></ul><ul><li>public void tearDown () throws Exception { } </li></ul><ul><li>} </li></ul>
  31. 34. jUnit 4.x <ul><li>public class CalculateServiceTest4 extends TestCase { </li></ul><ul><li>@BeforeClass </li></ul><ul><li>public static void setUpBeforeClass() throws Exception { } </li></ul><ul><li>@Before </li></ul><ul><li>public void beforeTest() throws Exception { } </li></ul><ul><li>@Test </li></ul><ul><li>public void theSum() { </li></ul><ul><li>Assert.assertEquals(8, new CalculateService().sum(3, 5)) ; </li></ul><ul><li> } </li></ul><ul><li>@After </li></ul><ul><li>public void afterTest() throws Exception { } </li></ul><ul><li>@AfterClass </li></ul><ul><li>public static void tearDownAfterClass() throws Exception { } </li></ul><ul><li>} </li></ul>
  32. 36. Spring 2.5 <ul><li><beans> </li></ul><ul><li><bean id=“newsDao” class=“javatwo.annotation.spring.NewsDao”/> </li></ul><ul><li><bean id=“newsService” class=“javatwo.annotation.spring.NewsService”> </li></ul><ul><li><property name=“newsDao”> </li></ul><ul><li><ref bean=“newsDao”/> </li></ul><ul><li></property> </li></ul><ul><li></bean> </li></ul><ul><li></beans> </li></ul><ul><li>@Service </li></ul><ul><li>public class NewsService { </li></ul><ul><li>@Autowired </li></ul><ul><li>private NewsDao newsDao; </li></ul><ul><li>} </li></ul>
  33. 37. Spring 3.0 <ul><li>@Named </li></ul><ul><li>public class NewsService { </li></ul><ul><li>@Inject </li></ul><ul><li>private NewsDao newsDao; </li></ul><ul><li>} </li></ul>
  34. 38. Spring 3.0 <ul><li>@Configuration </li></ul><ul><li>public class AppConfig { </li></ul><ul><li>private @Value(“#{sysprop.dbURL}”) String dbURL; </li></ul><ul><li>private @Value(“#{sysprop.username}”) String username; </li></ul><ul><li>private @Value(“#{sysprop.password}”) String password; </li></ul><ul><li>@Bean </li></ul><ul><li>public DataSource dataSource() { </li></ul><ul><li>return new DriverManagerDataSource(dbURL, username, password ); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  35. 39. MVC in Spring3.0 <ul><li>@Controller </li></ul><ul><li>@RequestMapping(“/news”) </li></ul><ul><li>public class NewsController { </li></ul><ul><li>@RequestMaaping(value=“{id}”, method=RequestMethod.GET) </li></ul><ul><li>public ModelAndView getById( @PathVariable(“id”) Long id) { </li></ul><ul><li>return … </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  36. 40. @Annotation Course 7 Javassist / ASM
  37. 41. Reflection <ul><li>Class myClass = MyClass.class; </li></ul><ul><li>Method[] myMethods = myClass. getMethods() ; </li></ul><ul><li>for(Method myMethod:myMethods) { </li></ul><ul><li>Annotation[] annotations = myMethod. getAnnotations (); </li></ul><ul><li>for( Annotation annotation:annotations) { </li></ul><ul><li>if( annotation instanceof MyAnno ) { </li></ul><ul><li>MyAnno myAnno = ( MyAnno ) annotation; </li></ul><ul><li>// do something </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  38. 42. Scan in ClassLoader <ul><li>scannotation.sf.net project ( based on javassist ) </li></ul><ul><li>URL[] urls = ClasspathUrlFinder.findClassPaths(); </li></ul><ul><li>AnnotationDB db = new AnnotationDB(); </li></ul><ul><li>db. scanArchives (urls); </li></ul><ul><li>Map<String, Set<String>> annoIndex = db.getAnnotationIndex(); </li></ul><ul><li>Set<String> aClassNames = annoIndex.get( “javatwo.annotation.MyAnno” ); </li></ul><ul><li>for( String aClassName:aClassNames ) { </li></ul><ul><li>.. </li></ul><ul><li>} </li></ul><ul><li>// in Web, WarUrlFinder. findWebInfClassesPath ( this .getServletContext()); </li></ul>
  39. 43. Get the Annotation values <ul><li>ClassPool pool = ClassPool.getDefault(); </li></ul><ul><li>pool.insertClassPath(new ClassClassPath(this.getClass)); </li></ul><ul><li>CtClass cc = pool.get(aClassName); </li></ul><ul><li>Object[] annos = cc.getAnnotations(); </li></ul><ul><li>for( Object anno : annos ) { </li></ul><ul><li>if( anno instanceof MyAnno ) { </li></ul><ul><li>MyAnno myAnno = (MyAnno) anno; </li></ul><ul><li>// do myAnno.var1(), myAnno.var2() </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  40. 44. Class R/W in ASM <ul><li>public class ScanMyDocVisitor implements ClassVisitor { </li></ul><ul><li>public AnnotationVisistor visitAnnotation(String desc, boolean visible) { </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>// in Controller </li></ul><ul><li>for(String className: classNames){ </li></ul><ul><li>ScanMyDocVisitor sv = new ScanMyDocVisitor (); </li></ul><ul><li>ClassReader cr = new ClassReader(Thread.currentThread().getContextClassLoader().getResouceAsStream(className)); </li></ul><ul><li>cr.accept(sv, 0); </li></ul><ul><li>List<AnnotationNode> annotationList = sv.getVisibleAnnotations(); </li></ul><ul><li>.. </li></ul><ul><li>} </li></ul>
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×